Compare commits
197 commits
1a0c415fa2
...
2dbfb07008
Author | SHA1 | Date | |
---|---|---|---|
|
2dbfb07008 | ||
|
f3fb69e7b9 | ||
|
8ebee968a6 | ||
|
d7960a6560 | ||
|
ab9e8be462 | ||
|
fa5d0e7d5b | ||
|
8c130d337e | ||
|
675caefb81 | ||
|
5a17e811e0 | ||
|
b4bd69fd9c | ||
|
006dbd5bc1 | ||
|
ecfaa950ea | ||
|
a417d049cb | ||
|
7d4b11b450 | ||
|
1d211bea98 | ||
|
61588be83f | ||
|
cd751a4c6a | ||
|
ce2abcdbca | ||
|
2b1fc4b561 | ||
|
7d58286cd9 | ||
|
afa6e0ee3c | ||
|
3dc861a21e | ||
|
5506a2c547 | ||
|
739b6d6533 | ||
|
c5e4e342bb | ||
|
c10a1f3568 | ||
|
1744f6b2c3 | ||
|
28185c12fc | ||
|
5a18d06a93 | ||
|
5e2953ff3f | ||
|
ebebd61343 | ||
|
63436be80e | ||
|
aed94cbbe6 | ||
|
efb055f5d6 | ||
|
1523fa2236 | ||
|
14956244cc | ||
|
e2e95f7d72 | ||
|
ccf94c5424 | ||
|
7a098800c5 | ||
|
d4d25c4e97 | ||
|
4e54b25b3e | ||
|
ff80e46eb0 | ||
|
1010443031 | ||
|
7c266be206 | ||
|
878f144bc1 | ||
|
7248b6833a | ||
|
454f177831 | ||
|
3dbbbb69e4 | ||
|
a198108222 | ||
|
8b264c38fe | ||
|
47c311e26b | ||
|
3f66ecf3be | ||
|
e23a7383f8 | ||
|
97456ff205 | ||
|
2e46d64ea0 | ||
|
a52c1cde9c | ||
|
c58dd5b471 | ||
|
f604ddacb5 | ||
|
5782caba92 | ||
|
6e6814ef35 | ||
|
c21e6ab952 | ||
|
6d2ae6a9e3 | ||
|
07856ee6b4 | ||
|
10aec7120a | ||
|
e998c059b6 | ||
|
084cd955a2 | ||
|
d272cecd55 | ||
|
30c4883aa6 | ||
|
89d57a3484 | ||
|
8c2678c82f | ||
|
a74c0e86c9 | ||
|
60ea36259e | ||
|
8e208a0f41 | ||
|
db4c9773b2 | ||
|
7f184bf6fa | ||
|
9386adb184 | ||
|
7bf2606120 | ||
|
5be9c9dbaf | ||
|
557d0e3aeb | ||
|
db5078d51c | ||
|
32f322a4c1 | ||
|
add913da27 | ||
|
f709ef96f7 | ||
|
f45dbf8b01 | ||
|
725f99c813 | ||
|
e13a31c4fe | ||
|
be8251ef0c | ||
|
2b3c1972db | ||
|
992b6eed1d | ||
|
45dda2c064 | ||
|
3554b61508 | ||
|
c5ca5bfdf8 | ||
|
7d91cc73de | ||
|
4c40bc164d | ||
|
cf74016077 | ||
|
76ccc52406 | ||
|
91a12295ff | ||
|
b692146533 | ||
|
d20cae82ad | ||
|
d83073f2a2 | ||
|
1789266859 | ||
|
b5de664ef6 | ||
|
46d3778ee8 | ||
|
675e5af2fd | ||
|
91a7217fb8 | ||
|
bce7e85e1f | ||
|
32688d34b6 | ||
|
527c17a8a7 | ||
|
29329f799d | ||
|
f673232e53 | ||
|
b233350e9f | ||
|
c064ebe231 | ||
|
85408fe437 | ||
|
8585a94f90 | ||
|
d50b9612a0 | ||
|
b270771f0f | ||
|
d4f0c1a8dc | ||
|
d196fbbd54 | ||
|
b5f2020313 | ||
|
74bcc33fdb | ||
|
0a3f1094ce | ||
|
3722aa5016 | ||
|
3f2481f424 | ||
|
789ae23ce5 | ||
|
ad127f4739 | ||
|
41030f596b | ||
|
7fd1db0ec6 | ||
|
54c530933d | ||
|
2a98e71b16 | ||
|
a268c5ffdc | ||
|
148f12580b | ||
|
948217da51 | ||
|
bbe6554bb0 | ||
|
9686eb9fb0 | ||
|
54a6748808 | ||
|
45c1d74750 | ||
|
44bdff664a | ||
|
bf4d19eed3 | ||
|
b4b9a6a34a | ||
|
791488982a | ||
|
d2429b1096 | ||
|
349fa08ece | ||
|
2294d6ffc3 | ||
|
4f55b1031b | ||
|
8ab7e6552b | ||
|
d425f2eaac | ||
|
8a37492ae6 | ||
|
e1dfcc35d7 | ||
|
cf26f3e881 | ||
|
a5b00e9199 | ||
|
018930b907 | ||
|
f74cc59530 | ||
|
25636c2442 | ||
|
c63a895672 | ||
|
2df2a8f123 | ||
|
9d770c0060 | ||
|
096b95a247 | ||
|
ae6c354232 | ||
|
b271b3aa27 | ||
|
b0a5cabe7d | ||
|
17d1236bdb | ||
|
d74b990f90 | ||
|
2c4b57ef50 | ||
|
ce2b7c8d4e | ||
|
6d3a4ea2b3 | ||
|
fff1d44878 | ||
|
e29e802b7c | ||
|
9e13ef4021 | ||
|
2f50b31c4f | ||
|
1a089e77a7 | ||
|
970d86472e | ||
|
f3c540cace | ||
|
e7e56bea65 | ||
|
496967e7f8 | ||
|
a725815d7a | ||
|
31217f0496 | ||
|
b60649ce38 | ||
|
2f6fda8b3c | ||
|
085fa74534 | ||
|
28b2fb04e4 | ||
|
b854a28422 | ||
|
523ce800f7 | ||
|
4821b431ae | ||
|
7f82bdb774 | ||
|
11103a2665 | ||
|
51d98996e5 | ||
|
373a1badce | ||
|
afc8093cd2 | ||
|
dd8f875d6b | ||
|
33d2a122f9 | ||
|
69e4254dcc | ||
|
7295b2c40c | ||
|
ce9939b4c0 | ||
|
58737ba70f | ||
|
26e8180e8d | ||
|
c81a9d1ddd | ||
|
78b969cb19 |
166 changed files with 33648 additions and 15965 deletions
63
CHANGELOG
63
CHANGELOG
|
@ -1,3 +1,63 @@
|
|||
Version 2023.05 (2023-05-23)
|
||||
Friendica Core
|
||||
Updates to the translations HU, PL
|
||||
Updates to the themes (frio, vier) [MrPetovan, xundeenergie]
|
||||
Updates to the documentation [MrPetovan]
|
||||
Improved the probing of remote accounts [Annando, ne20002]
|
||||
Improved the OWA implementation [annando, abanink]
|
||||
Improved the database handling [nupplaphil]
|
||||
Improved the ATOM feed handling, added OPML [annando]
|
||||
Improved the audience handling of forum posting [annando]
|
||||
General code cleanup [annando, MrPetovan, nupplaphil, Raroun]
|
||||
Fixed HTML escaping for notification messages [MrPetovan, nupplaphil]
|
||||
Fixed the feed body import [annando]
|
||||
Fixed meaning of the limit parameter in the Mastodon API [annando]
|
||||
Fixed a bug in the tag cloud [MrPetovan]
|
||||
Fixed the magic links in photo menu [MrPetovan]
|
||||
Added new hook for addons (support_probe) [annando]
|
||||
Added support for special characters in category links [MrPetovan]
|
||||
Added possibility to collapse postings [annando]
|
||||
Added the emojipicker to the core [annando]
|
||||
Added a fancybox to view images in network stream [annando]
|
||||
|
||||
Friendica Addons
|
||||
Updates to the translations DE, HU
|
||||
bluesky
|
||||
Added the bluesky connector [annando]
|
||||
mailstream
|
||||
General code cleanup [mexon]
|
||||
tumblr
|
||||
Added OAuth2 support [ænnando]
|
||||
Added un/follow, un/block functionality [annando]
|
||||
Added probe [annando]
|
||||
Added the possibility for users to follow tags [annando]
|
||||
emojipicker, fancybox
|
||||
The functionality of this addon has been moved to the
|
||||
core for the frio and vier themes. [annando]
|
||||
|
||||
Closed Issues
|
||||
12439, 12760, 12788, 12824, 12979, 13016, 13026, 13027, 13036,
|
||||
13052, 13073, 13082, 13089, 13102, 13104, 13108
|
||||
|
||||
Version 2023.04-1 (2023-04-27)
|
||||
Friendica Core
|
||||
Updates to the translations AR, CA, CS, DE, ES, FR, GD, HU, IT, JA, NL, PL, RU
|
||||
Fixed a bug that broke the private messages web interface [MrPetovan]
|
||||
|
||||
Friendica Addons
|
||||
tumblr
|
||||
Added follow, unfollow, cross posting [annando]
|
||||
Added the possibility to import the remote timeline [annando]
|
||||
**Breaking** Switch to OAuth2 [annando]
|
||||
Node admins have to change the redirect URL in the settings to the
|
||||
OAuth2 specific one. social.example.com/tumblr/redirect
|
||||
mailstream
|
||||
Various modernization [mexon]
|
||||
Include post media [mexon]
|
||||
|
||||
Closed Issues
|
||||
13026
|
||||
|
||||
Version 2023.04 (2023-04-23)
|
||||
Friendica Core
|
||||
Updates to the translations AR, BG, CA, CS, DA, DE, EO, ES, ET, FR, GD, HU, IS, IT, JA, NL, PL, RU, SV
|
||||
|
@ -39,9 +99,6 @@ Version 2023.04 (2023-04-23)
|
|||
|
||||
Friendica Addons
|
||||
Updates to the translations AR, CS, DE, ES, HU, IS, IT, NL, PL, RU, SV
|
||||
mailstream
|
||||
Various modernization [mexon]
|
||||
Include post media [mexon]
|
||||
securemail
|
||||
Updated the phpseclib dependency [MrPetovan]
|
||||
twitter
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
If you want to contribute to the project, you don’t need to have coding experience. There are a number of tasks listed in the issue tracker with the label “[Junior Jobs](https://github.com/friendica/friendica/issues?q=is%3Aopen+is%3Aissue+label%3A%22Junior+Jobs%22)” we think are good for new contributors. But you are by no means limited to these – if you find a solution to a problem (even a new one) please make a pull request at [github](https://github.com/friendica/friendica) or let us know in the [development forum](https://forum.friendi.ca/profile/developers).
|
||||
|
||||
Contribution to Friendica is also not limited to coding. Any contribution to the [documentation](https://github.com/friendica/friendica/tree/develop/doc), the [translation](https://www.transifex.com/Friendica/friendica/dashboard/) or advertisement materials is welcome or reporting a problem. You don’t need to deal with Git(Hub) or Transifex if you don’t like to. Just [get in touch](https://forum.friendi.ca/profile/helpers) with us and we will get the materials to the appropriate places.
|
||||
Contribution to Friendica is also not limited to coding. Any contribution to the [documentation](https://github.com/friendica/friendica/tree/develop/doc), the [translation](https://app.transifex.com/Friendica/friendica/dashboard/) or advertisement materials is welcome or reporting a problem. You don’t need to deal with Git(Hub) or Transifex if you don’t like to. Just [get in touch](https://forum.friendi.ca/profile/helpers) with us and we will get the materials to the appropriate places.
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2023.04
|
||||
2023.05
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2023.04 (Giant Rhubarb)
|
||||
-- Friendica 2023.05 (Giant Rhubarb)
|
||||
-- DB_UPDATE_VERSION 1518
|
||||
-- ------------------------------------------
|
||||
|
||||
|
|
|
@ -638,6 +638,14 @@ Hook data:
|
|||
- **uid** (input): the user id to revoke the block for.
|
||||
- **result** (output): a boolean value indicating wether the operation was successful or not.
|
||||
|
||||
### support_probe
|
||||
|
||||
Called to assert whether a connector addon provides probing capabilities.
|
||||
|
||||
Hook data:
|
||||
- **protocol** (input): shorthand for the protocol. List of values is available in `src/Core/Protocol.php`.
|
||||
- **result** (output): should be true if the connector provides follow capabilities, left alone otherwise.
|
||||
|
||||
### storage_instance
|
||||
|
||||
Called when a custom storage is used (e.g. webdav_storage)
|
||||
|
@ -907,6 +915,7 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
|
|||
Hook::callAll('revoke_follow', $hook_data);
|
||||
Hook::callAll('block', $hook_data);
|
||||
Hook::callAll('unblock', $hook_data);
|
||||
Hook::callAll('support_probe', $hook_data);
|
||||
|
||||
### src/Core/Logger/Factory.php
|
||||
|
||||
|
|
|
@ -36,7 +36,11 @@ Have a look into your <tt>config/local.config.php</tt> and fix your email addres
|
|||
Yes.
|
||||
You just have to list more then one email address in the
|
||||
<tt>config/local.config.php</tt> file.
|
||||
The listed emails need to be separated by a comma.
|
||||
The listed emails need to be separated by a comma like this:
|
||||
|
||||
```php
|
||||
'admin_email' => 'mail1@example.com,mail2@example.com',
|
||||
```
|
||||
|
||||
<a name="dbupdate">
|
||||
### The Database structure seems not to be updated. What can I do?
|
||||
|
@ -48,4 +52,4 @@ You can manually execute the structure update from the CLI in the base directory
|
|||
|
||||
bin/console dbstructure update
|
||||
|
||||
if there occur any errors, please contact the [support forum](https://forum.friendi.ca/profile/helpers).
|
||||
if there occur any errors, please contact the [support forum](https://forum.friendi.ca/profile/helpers).
|
||||
|
|
|
@ -397,6 +397,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
Hook::callAll('revoke_follow', $hook_data);
|
||||
Hook::callAll('block', $hook_data);
|
||||
Hook::callAll('unblock', $hook_data);
|
||||
Hook::callAll('support_probe', $hook_data);
|
||||
|
||||
### src/Core/Logger/Factory.php
|
||||
|
||||
|
|
|
@ -39,7 +39,11 @@ Bitte aktualisiere deine E-Mail Adresse in der <tt>config/local.config.php</tt>
|
|||
|
||||
Ja.
|
||||
Du kannst in der <tt>config/local.config.php</tt> Datei mehrere E-Mail Adressen auflisten.
|
||||
Die aufgelisteten Adressen werden mit Kommata von einander getrennt.
|
||||
Die aufgelisteten Adressen werden wie folgt durch Kommas voneinander getrennt:
|
||||
|
||||
```php
|
||||
'admin_email' => 'mail1@example.com,mail2@example.com',
|
||||
```
|
||||
|
||||
<a name="dbupdate">
|
||||
### Die Datenbank Struktur schein nicht aktuell zu sein. Was kann ich tun?
|
||||
|
@ -52,4 +56,4 @@ Starte dazu bitte vom Grundverzeichnis deiner Friendica Instanz folgendes Komman
|
|||
|
||||
bin/console dbstructure update
|
||||
|
||||
sollten bei der Ausführung Fehler auftreten, kontaktiere bitte das [Support Forum](https://forum.friendi.ca/profile/helpers).
|
||||
sollten bei der Ausführung Fehler auftreten, kontaktiere bitte das [Support Forum](https://forum.friendi.ca/profile/helpers).
|
||||
|
|
|
@ -9,7 +9,7 @@ The Friendica translation process is based on `gettext` PO files.
|
|||
|
||||
Basic workflow:
|
||||
1. `xgettext` is used to collect translation strings across the project in the authoritative PO file located in `view/lang/C/messages.po`.
|
||||
2. This file makes translations strings available at [the Transifex Friendica page](https://www.transifex.com/Friendica/friendica/dashboard/).
|
||||
2. This file makes translations strings available at [the Transifex Friendica page](https://app.transifex.com/Friendica/friendica/dashboard/).
|
||||
3. The translation itself is done at Transifex by volunteers.
|
||||
4. The resulting PO files by languages are manually updated in `view/lang/<language>/messages.po`.
|
||||
5. PO files are converted to PHP arrays in `view/lang/<language>/strings.php` that are ultimately used by Friendica to display the translations.
|
||||
|
@ -17,7 +17,7 @@ Basic workflow:
|
|||
## Translate Friendica in your favorite language
|
||||
|
||||
Thank you for your interest in improving Friendica's translation!
|
||||
Please register a free Transifex account and ask over at [the Transifex Friendica page](https://www.transifex.com/Friendica/friendica/dashboard/) to join the translation team for your favorite language.
|
||||
Please register a free Transifex account and ask over at [the Transifex Friendica page](https://app.transifex.com/Friendica/friendica/dashboard/) to join the translation team for your favorite language.
|
||||
|
||||
As a rule of thumb, we add support for a language in Friendica when at least 50% of the strings have been translated to avoid a scattered experience.
|
||||
For addons, we add support for a language when if we already support the language in Friendica.
|
||||
|
@ -66,11 +66,11 @@ To use it, first create a configuration file with your credentials.
|
|||
On Linux this file should be placed into your home directory `~/.transifexrc`.
|
||||
The content of the file should be something like the following:
|
||||
|
||||
[https://www.transifex.com]
|
||||
[https://app.transifex.com]
|
||||
username = user
|
||||
token =
|
||||
password = p@ssw0rd
|
||||
hostname = https://www.transifex.com
|
||||
hostname = https://app.transifex.com
|
||||
|
||||
Since Friendica version 3.5.1 we ship configuration files for the Transifex client in the core repository and the addon repository in `.tx/config`.
|
||||
To update the PO files after you have translated strings of e.g. Esperanto on the Transifex website you can use `tx` to download the updated PO file in the right location.
|
||||
|
|
BIN
images/bluesky.jpg
Normal file
BIN
images/bluesky.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
16
mod/item.php
16
mod/item.php
|
@ -384,6 +384,22 @@ function item_content(App $a)
|
|||
|
||||
Contact\User::setIgnored($item['author-id'], DI::userSession()->getLocalUserId(), true);
|
||||
|
||||
if (DI::mode()->isAjax()) {
|
||||
// ajax return: [<item id>, 0 (no perm) | <owner id>]
|
||||
System::jsonExit([intval($args->get(2)), DI::userSession()->getLocalUserId()]);
|
||||
} else {
|
||||
item_redirect_after_action($item, $args->get(3));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'collapse':
|
||||
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), ['guid', 'author-id', 'parent', 'gravity'], ['id' => $args->get(2)]);
|
||||
if (empty($item['author-id'])) {
|
||||
throw new HTTPException\NotFoundException('Item not found');
|
||||
}
|
||||
|
||||
Contact\User::setCollapsed($item['author-id'], DI::userSession()->getLocalUserId(), true);
|
||||
|
||||
if (DI::mode()->isAjax()) {
|
||||
// ajax return: [<item id>, 0 (no perm) | <owner id>]
|
||||
System::jsonExit([intval($args->get(2)), DI::userSession()->getLocalUserId()]);
|
||||
|
|
|
@ -54,11 +54,10 @@ function message_init(App $a)
|
|||
'$tabs' => $tabs,
|
||||
'$new' => $new,
|
||||
]);
|
||||
$base = DI::baseUrl();
|
||||
|
||||
$head_tpl = Renderer::getMarkupTemplate('message-head.tpl');
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($head_tpl, [
|
||||
'$base' => $base
|
||||
'$base' => (string)DI::baseUrl()
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -375,9 +375,7 @@ function photos_post(App $a)
|
|||
$arr['visible'] = 0;
|
||||
$arr['origin'] = 1;
|
||||
|
||||
$arr['body'] = '[url=' . DI::baseUrl() . '/photos/' . $user['nickname'] . '/image/' . $photo['resource-id'] . ']'
|
||||
. '[img]' . DI::baseUrl() . '/photo/' . $photo['resource-id'] . '-' . $photo['scale'] . '.'. $ext . '[/img]'
|
||||
. '[/url]';
|
||||
$arr['body'] = Images::getBBCodeByResource($photo['resource-id'], $user['nickname'], $photo['scale'], $ext);
|
||||
|
||||
$item_id = Item::insert($arr);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Contact: mailto:info@friendi.ca
|
||||
|
||||
Expires: 2023-12-31T23:59:59Z
|
||||
Expires: 2024-04-30T23:59:59Z
|
||||
|
||||
Preferred-Languages: en
|
||||
|
||||
|
|
13
src/App.php
13
src/App.php
|
@ -64,7 +64,7 @@ class App
|
|||
{
|
||||
const PLATFORM = 'Friendica';
|
||||
const CODENAME = 'Giant Rhubarb';
|
||||
const VERSION = '2023.04';
|
||||
const VERSION = '2023.05';
|
||||
|
||||
// Allow themes to control internal parameters
|
||||
// by changing App values in theme.php
|
||||
|
@ -335,7 +335,13 @@ class App
|
|||
*/
|
||||
protected function load(DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
|
||||
{
|
||||
set_time_limit(0);
|
||||
if ($this->config->get('system', 'ini_max_execution_time') !== false) {
|
||||
set_time_limit((int)$this->config->get('system', 'ini_max_execution_time'));
|
||||
}
|
||||
|
||||
if ($this->config->get('system', 'ini_pcre_backtrack_limit') !== false) {
|
||||
ini_set('pcre.backtrack_limit', (int)$this->config->get('system', 'ini_pcre_backtrack_limit'));
|
||||
}
|
||||
|
||||
// Normally this constant is defined - but not if "pcntl" isn't installed
|
||||
if (!defined('SIGTERM')) {
|
||||
|
@ -345,9 +351,6 @@ class App
|
|||
// Ensure that all "strtotime" operations do run timezone independent
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
// This has to be quite large to deal with embedded private photos
|
||||
ini_set('pcre.backtrack_limit', 500000);
|
||||
|
||||
set_include_path(
|
||||
get_include_path() . PATH_SEPARATOR
|
||||
. $this->getBasePath() . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR
|
||||
|
|
|
@ -244,9 +244,10 @@ class Page implements ArrayAccess
|
|||
*/
|
||||
$this->page['htmlhead'] = Renderer::replaceMacros($tpl, [
|
||||
'$l10n' => [
|
||||
'delitem' => $l10n->t('Delete this item?'),
|
||||
'blockAuthor' => $l10n->t('Block this author? They won\'t be able to follow you nor see your public posts, and you won\'t be able to see their posts and their notifications.'),
|
||||
'ignoreAuthor' => $l10n->t('Ignore this author? You won\'t be able to see their posts and their notifications.'),
|
||||
'delitem' => $l10n->t('Delete this item?'),
|
||||
'blockAuthor' => $l10n->t('Block this author? They won\'t be able to follow you nor see your public posts, and you won\'t be able to see their posts and their notifications.'),
|
||||
'ignoreAuthor' => $l10n->t('Ignore this author? You won\'t be able to see their posts and their notifications.'),
|
||||
'collapseAuthor' => $l10n->t('Collapse this author\'s posts?'),
|
||||
|
||||
'likeError' => $l10n->t('Like not successful'),
|
||||
'dislikeError' => $l10n->t('Dislike not successful'),
|
||||
|
|
|
@ -141,6 +141,7 @@ class ContactSelector
|
|||
Protocol::ACTIVITYPUB => DI::l10n()->t('ActivityPub'),
|
||||
Protocol::PNUT => DI::l10n()->t('pnut'),
|
||||
Protocol::TUMBLR => DI::l10n()->t('Tumblr'),
|
||||
Protocol::BLUESKY => DI::l10n()->t('Bluesky'),
|
||||
];
|
||||
|
||||
Hook::callAll('network_to_name', $nets);
|
||||
|
@ -212,6 +213,7 @@ class ContactSelector
|
|||
Protocol::ACTIVITYPUB => 'activitypub',
|
||||
Protocol::PNUT => 'file-text-o', /// @todo
|
||||
Protocol::TUMBLR => 'tumblr',
|
||||
Protocol::BLUESKY => 'circle', /// @todo
|
||||
];
|
||||
|
||||
$platform_icons = ['diaspora' => 'diaspora', 'friendica' => 'friendica', 'friendika' => 'friendica',
|
||||
|
|
|
@ -365,6 +365,7 @@ class Conversation
|
|||
'$editalic' => $this->l10n->t('Italic'),
|
||||
'$eduline' => $this->l10n->t('Underline'),
|
||||
'$edquote' => $this->l10n->t('Quote'),
|
||||
'$edemojis' => $this->l10n->t('Add emojis'),
|
||||
'$edcode' => $this->l10n->t('Code'),
|
||||
'$edimg' => $this->l10n->t('Image'),
|
||||
'$edurl' => $this->l10n->t('Link'),
|
||||
|
@ -873,6 +874,9 @@ class Conversation
|
|||
case ItemModel::PR_BCC:
|
||||
$row['direction'] = ['direction' => 7, 'title' => $this->l10n->t('You had been addressed (%s).', 'bcc')];
|
||||
break;
|
||||
case ItemModel::PR_AUDIENCE:
|
||||
$row['direction'] = ['direction' => 7, 'title' => $this->l10n->t('You had been addressed (%s).', 'audience')];
|
||||
break;
|
||||
case ItemModel::PR_FOLLOWER:
|
||||
$row['direction'] = ['direction' => 6, 'title' => $this->l10n->t('You are following %s.', $row['causer-name'] ?: $row['author-name'])];
|
||||
break;
|
||||
|
|
|
@ -96,32 +96,33 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Return array with details for categories and folders for an item
|
||||
* Lists categories and folders for an item
|
||||
*
|
||||
* @param array $item
|
||||
* @param int $uid
|
||||
* @return [array, array]
|
||||
*
|
||||
* @return array
|
||||
* [
|
||||
* [ // categories array
|
||||
* {
|
||||
* 'name': 'category name',
|
||||
* 'removeurl': 'url to remove this category',
|
||||
* 'first': 'is the first in this array? true/false',
|
||||
* 'last': 'is the last in this array? true/false',
|
||||
* } ,
|
||||
* ....
|
||||
* ],
|
||||
* [ //folders array
|
||||
* {
|
||||
* 'name': 'folder name',
|
||||
* 'removeurl': 'url to remove this folder',
|
||||
* 'first': 'is the first in this array? true/false',
|
||||
* 'last': 'is the last in this array? true/false',
|
||||
* } ,
|
||||
* ....
|
||||
* ]
|
||||
* ]
|
||||
* [ // categories array
|
||||
* {
|
||||
* 'name': 'category name',
|
||||
* 'removeurl': 'url to remove this category',
|
||||
* 'first': 'is the first in this array? true/false',
|
||||
* 'last': 'is the last in this array? true/false',
|
||||
* },
|
||||
* ...
|
||||
* ],
|
||||
* [ //folders array
|
||||
* {
|
||||
* 'name': 'folder name',
|
||||
* 'removeurl': 'url to remove this folder',
|
||||
* 'first': 'is the first in this array? true/false',
|
||||
* 'last': 'is the last in this array? true/false',
|
||||
* } ,
|
||||
* ...
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function determineCategoriesTerms(array $item, int $uid = 0): array
|
||||
{
|
||||
|
@ -135,9 +136,9 @@ class Item
|
|||
return [$categories, $folders];
|
||||
}
|
||||
|
||||
foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::CATEGORY) as $savedFolderName) {
|
||||
foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid) as $savedFolderName) {
|
||||
if (!empty($item['author-link'])) {
|
||||
$url = $item['author-link'] . "?category=" . rawurlencode($savedFolderName);
|
||||
$url = $item['author-link'] . '/conversations?category=' . rawurlencode($savedFolderName);
|
||||
} else {
|
||||
$url = '#';
|
||||
}
|
||||
|
@ -377,7 +378,11 @@ class Item
|
|||
'url' => $item['author-link'],
|
||||
];
|
||||
$profile_link = Contact::magicLinkByContact($author, $item['author-link']);
|
||||
$sparkle = (strpos($profile_link, 'contact/redir/') === 0);
|
||||
if (strpos($profile_link, 'contact/redir/') === 0) {
|
||||
$status_link = $profile_link . '?' . http_build_query(['url' => $item['author-link'] . '/status']);
|
||||
$photos_link = $profile_link . '?' . http_build_query(['url' => $item['author-link'] . '/photos']);
|
||||
$profile_link = $profile_link . '?' . http_build_query(['url' => $item['author-link'] . '/profile']);
|
||||
}
|
||||
|
||||
$cid = 0;
|
||||
$pcid = $item['author-id'];
|
||||
|
@ -391,12 +396,6 @@ class Item
|
|||
$rel = $contact['rel'];
|
||||
}
|
||||
|
||||
if ($sparkle) {
|
||||
$status_link = $profile_link . '/status';
|
||||
$photos_link = $profile_link . '/photos';
|
||||
$profile_link = $profile_link . '/profile';
|
||||
}
|
||||
|
||||
if (!empty($pcid)) {
|
||||
$contact_url = 'contact/' . $pcid;
|
||||
$posts_link = $contact_url . '/posts';
|
||||
|
|
|
@ -106,7 +106,7 @@ class Smilies
|
|||
|
||||
];
|
||||
|
||||
$baseUrl = DI::baseUrl();
|
||||
$baseUrl = (string)DI::baseUrl();
|
||||
|
||||
$icons = [
|
||||
'<img class="smiley" src="' . $baseUrl . '/images/smiley-heart.gif" alt="<3" title="<3" />',
|
||||
|
|
|
@ -1403,12 +1403,32 @@ class BBCode
|
|||
}
|
||||
|
||||
// Check for headers
|
||||
$text = preg_replace("(\[h1\](.*?)\[\/h1\])ism", '</p><h1>$1</h1><p>', $text);
|
||||
$text = preg_replace("(\[h2\](.*?)\[\/h2\])ism", '</p><h2>$1</h2><p>', $text);
|
||||
$text = preg_replace("(\[h3\](.*?)\[\/h3\])ism", '</p><h3>$1</h3><p>', $text);
|
||||
$text = preg_replace("(\[h4\](.*?)\[\/h4\])ism", '</p><h4>$1</h4><p>', $text);
|
||||
$text = preg_replace("(\[h5\](.*?)\[\/h5\])ism", '</p><h5>$1</h5><p>', $text);
|
||||
$text = preg_replace("(\[h6\](.*?)\[\/h6\])ism", '</p><h6>$1</h6><p>', $text);
|
||||
|
||||
if ($simple_html == self::INTERNAL) {
|
||||
//Ensure to always start with <h4> if possible
|
||||
$heading_count = 0;
|
||||
for ($level = 6; $level > 0; $level--) {
|
||||
if (preg_match("(\[h$level\].*?\[\/h$level\])ism", $text)) {
|
||||
$heading_count++;
|
||||
}
|
||||
}
|
||||
if ($heading_count > 0) {
|
||||
$heading = min($heading_count + 3, 6);
|
||||
for ($level = 6; $level > 0; $level--) {
|
||||
if (preg_match("(\[h$level\].*?\[\/h$level\])ism", $text)) {
|
||||
$text = preg_replace("(\[h$level\](.*?)\[\/h$level\])ism", "</p><h$heading>$1</h$heading><p>", $text);
|
||||
$heading--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$text = preg_replace("(\[h1\](.*?)\[\/h1\])ism", '</p><h1>$1</h1><p>', $text);
|
||||
$text = preg_replace("(\[h2\](.*?)\[\/h2\])ism", '</p><h2>$1</h2><p>', $text);
|
||||
$text = preg_replace("(\[h3\](.*?)\[\/h3\])ism", '</p><h3>$1</h3><p>', $text);
|
||||
$text = preg_replace("(\[h4\](.*?)\[\/h4\])ism", '</p><h4>$1</h4><p>', $text);
|
||||
$text = preg_replace("(\[h5\](.*?)\[\/h5\])ism", '</p><h5>$1</h5><p>', $text);
|
||||
$text = preg_replace("(\[h6\](.*?)\[\/h6\])ism", '</p><h6>$1</h6><p>', $text);
|
||||
}
|
||||
|
||||
// Check for paragraph
|
||||
$text = preg_replace("(\[p\](.*?)\[\/p\])ism", '<p>$1</p>', $text);
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace Friendica\Content\Text;
|
|||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Friendica\Protocol\HTTP\MediaType;
|
||||
use Friendica\Content\Widget\ContactBlock;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
|
|
|
@ -26,7 +26,6 @@ use Friendica\DI;
|
|||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
class Plaintext
|
||||
{
|
||||
|
|
|
@ -178,6 +178,10 @@ class Widget
|
|||
$baseUrl = trim($baseUrl, '?') . '?';
|
||||
}
|
||||
|
||||
array_walk($options, function (&$value) {
|
||||
$value['ref'] = rawurlencode($value['ref']);
|
||||
});
|
||||
|
||||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/filter.tpl'), [
|
||||
'$type' => $type,
|
||||
'$title' => $title,
|
||||
|
|
|
@ -58,7 +58,7 @@ class TagCloud
|
|||
foreach ($r as $rr) {
|
||||
$tags[] = [
|
||||
'level' => $rr[2],
|
||||
'url' => $url . '?tag=' . urlencode($rr[0]),
|
||||
'url' => $url . '/conversations?tag=' . urlencode($rr[0]),
|
||||
'name' => $rr[0],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -66,8 +66,9 @@ class ACL
|
|||
|
||||
$tpl = Renderer::getMarkupTemplate('acl/message_recipient.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$contacts' => json_encode($contacts),
|
||||
'$selected' => $selected,
|
||||
'$contacts' => $contacts,
|
||||
'$contacts_json' => json_encode($contacts),
|
||||
'$selected' => $selected,
|
||||
]);
|
||||
|
||||
Hook::callAll(DI::args()->getModuleName() . '_post_recipient', $o);
|
||||
|
|
|
@ -57,6 +57,7 @@ class Protocol
|
|||
const TWITTER = 'twit'; // Twitter
|
||||
const DISCOURSE = 'dscs'; // Discourse
|
||||
const TUMBLR = 'tmbl'; // Tumblr
|
||||
const BLUESKY = 'bsky'; // Bluesky
|
||||
|
||||
// Dead protocols
|
||||
const APPNET = 'apdn'; // app.net - Dead protocol
|
||||
|
@ -305,4 +306,31 @@ class Protocol
|
|||
|
||||
return $hook_data['result'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the provided protocol supports probing for contacts
|
||||
*
|
||||
* @param $protocol
|
||||
* @return bool
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function supportsProbe($protocol): bool
|
||||
{
|
||||
// "Mail" can only be probed for a specific user in a specific condition, so we are ignoring it here.
|
||||
if ($protocol == self::MAIL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (in_array($protocol, array_merge(self::NATIVE_SUPPORT, [self::ZOT, self::PHANTOM]))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$hook_data = [
|
||||
'protocol' => $protocol,
|
||||
'result' => null
|
||||
];
|
||||
Hook::callAll('support_probe', $hook_data);
|
||||
|
||||
return $hook_data['result'] === true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,40 +55,42 @@ class Search
|
|||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function getContactsFromProbe(string $user): ResultList
|
||||
public static function getContactsFromProbe(string $user, $only_forum = false): ResultList
|
||||
{
|
||||
$emptyResultList = new ResultList(1, 0, 1);
|
||||
$emptyResultList = new ResultList();
|
||||
|
||||
if ((filter_var($user, FILTER_VALIDATE_EMAIL) && Network::isEmailDomainValid($user)) ||
|
||||
(substr(Strings::normaliseLink($user), 0, 7) == 'http://')) {
|
||||
|
||||
$user_data = Contact::getByURL($user);
|
||||
if (empty($user_data)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
if (!in_array($user_data['network'], Protocol::FEDERATED)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
$contactDetails = Contact::getByURLForUser($user_data['url'] ?? '', DI::userSession()->getLocalUserId());
|
||||
|
||||
$result = new ContactResult(
|
||||
$user_data['name'] ?? '',
|
||||
$user_data['addr'] ?? '',
|
||||
($contactDetails['addr'] ?? '') ?: ($user_data['url'] ?? ''),
|
||||
new Uri($user_data['url'] ?? ''),
|
||||
$user_data['photo'] ?? '',
|
||||
$user_data['network'] ?? '',
|
||||
$contactDetails['cid'] ?? 0,
|
||||
$user_data['id'] ?? 0,
|
||||
$user_data['tags'] ?? ''
|
||||
);
|
||||
|
||||
return new ResultList(1, 1, 1, [$result]);
|
||||
} else {
|
||||
if (empty(parse_url($user, PHP_URL_SCHEME)) && !(filter_var($user, FILTER_VALIDATE_EMAIL) || Network::isEmailDomainValid($user))) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
$user_data = Contact::getByURL($user);
|
||||
if (empty($user_data)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
if ($only_forum && ($user_data['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
if (!Protocol::supportsProbe($user_data['network'])) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
$contactDetails = Contact::getByURLForUser($user_data['url'], DI::userSession()->getLocalUserId());
|
||||
|
||||
$result = new ContactResult(
|
||||
$user_data['name'],
|
||||
$user_data['addr'],
|
||||
$user_data['addr'] ?: $user_data['url'],
|
||||
new Uri($user_data['url']),
|
||||
$user_data['photo'],
|
||||
$user_data['network'],
|
||||
$contactDetails['cid'] ?? 0,
|
||||
$user_data['id'],
|
||||
$user_data['keywords']
|
||||
);
|
||||
|
||||
return new ResultList(1, 1, 1, [$result]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +131,7 @@ class Search
|
|||
|
||||
$resultList = new ResultList(
|
||||
($results['page'] ?? 0) ?: 1,
|
||||
$results['count'] ?? 0,
|
||||
$results['count'] ?? 0,
|
||||
($results['itemsperpage'] ?? 0) ?: 30
|
||||
);
|
||||
|
||||
|
@ -174,7 +176,7 @@ class Search
|
|||
|
||||
$contacts = Contact::searchByName($search, $type == self::TYPE_FORUM ? 'community' : '', true);
|
||||
|
||||
$resultList = new ResultList($start, $itemPage, count($contacts));
|
||||
$resultList = new ResultList($start, count($contacts), $itemPage);
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
$result = new ContactResult(
|
||||
|
|
|
@ -129,20 +129,6 @@ class Update
|
|||
DI::lock()->release('dbupdate', true);
|
||||
}
|
||||
|
||||
if (!DBStructure::existsTable('config')) {
|
||||
DBA::e(<<<EOF
|
||||
CREATE TABLE IF NOT EXISTS `config` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT '',
|
||||
`cat` varbinary(50) NOT NULL DEFAULT '' COMMENT 'The category of the entry',
|
||||
`k` varbinary(50) NOT NULL DEFAULT '' COMMENT 'The key of the entry',
|
||||
`v` mediumtext COMMENT '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE INDEX `cat_k` (`cat`,`k`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage';
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
$build = DI::config()->get('system', 'build');
|
||||
|
||||
if (empty($build)) {
|
||||
|
|
|
@ -151,8 +151,8 @@ class Cron
|
|||
// We are acquiring the two locks from the worker to avoid locking problems
|
||||
if (DI::lock()->acquire(Worker::LOCK_PROCESS, 10)) {
|
||||
if (DI::lock()->acquire(Worker::LOCK_WORKER, 10)) {
|
||||
DBA::e("OPTIMIZE TABLE `workerqueue`");
|
||||
DBA::e("OPTIMIZE TABLE `process`");
|
||||
DBA::optimizeTable('workerqueue');
|
||||
DBA::optimizeTable('process');
|
||||
DI::lock()->release(Worker::LOCK_WORKER);
|
||||
}
|
||||
DI::lock()->release(Worker::LOCK_PROCESS);
|
||||
|
@ -197,7 +197,7 @@ class Cron
|
|||
// Optimizing this table only last seconds
|
||||
if (DI::config()->get('system', 'optimize_tables')) {
|
||||
Logger::info('Optimize start');
|
||||
DBA::e("OPTIMIZE TABLE `post-delivery`");
|
||||
DBA::optimizeTable('post-delivery');
|
||||
Logger::info('Optimize end');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -821,6 +821,27 @@ class DBA
|
|||
return DI::dba()->processlist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes tables
|
||||
*
|
||||
* @param string $table a given table
|
||||
*
|
||||
* @return bool True, if successfully optimized, otherwise false
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function optimizeTable(string $table): bool
|
||||
{
|
||||
return DI::dba()->optimizeTable($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill sleeping database processes
|
||||
*/
|
||||
public static function deleteSleepingProcesses()
|
||||
{
|
||||
DI::dba()->deleteSleepingProcesses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a database variable
|
||||
*
|
||||
|
|
|
@ -57,6 +57,18 @@ class DBStructure
|
|||
echo DI::l10n()->t('The database version had been set to %s.', $version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a specific table
|
||||
*
|
||||
* @param string $table the table name
|
||||
*
|
||||
* @return bool true if possible, otherwise false
|
||||
*/
|
||||
public static function dropTable(string $table): bool
|
||||
{
|
||||
return DBA::isResult(DBA::e('DROP TABLE ' . DBA::quoteIdentifier($table) . ';'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop unused tables
|
||||
*
|
||||
|
@ -94,8 +106,7 @@ class DBStructure
|
|||
$sql = 'DROP TABLE ' . DBA::quoteIdentifier($table) . ';';
|
||||
echo $sql . "\n";
|
||||
|
||||
$result = DBA::e($sql);
|
||||
if (!DBA::isResult($result)) {
|
||||
if (!static::dropTable($table)) {
|
||||
self::printUpdateError($sql);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -80,6 +80,8 @@ class Database
|
|||
protected $dbaDefinition;
|
||||
/** @var ViewDefinition */
|
||||
protected $viewDefinition;
|
||||
/** @var string|null */
|
||||
private $currentTable;
|
||||
|
||||
public function __construct(IManageConfigValues $config, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
|
||||
{
|
||||
|
@ -503,7 +505,7 @@ class Database
|
|||
*/
|
||||
public function p(string $sql)
|
||||
{
|
||||
|
||||
$this->currentTable = null;
|
||||
$this->profiler->startRecording('database');
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
|
@ -973,8 +975,8 @@ class Database
|
|||
switch ($this->driver) {
|
||||
case self::PDO:
|
||||
$columns = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!empty($stmt->table) && is_array($columns)) {
|
||||
$columns = $this->castFields($stmt->table, $columns);
|
||||
if (!empty($this->currentTable) && is_array($columns)) {
|
||||
$columns = $this->castFields($this->currentTable, $columns);
|
||||
}
|
||||
break;
|
||||
case self::MYSQLI:
|
||||
|
@ -1357,6 +1359,15 @@ class Database
|
|||
}
|
||||
|
||||
$fields = $this->castFields($table, $fields);
|
||||
$direct_fields = [];
|
||||
|
||||
foreach ($fields as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
$direct_fields[] = $value;
|
||||
unset($fields[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$table_string = DBA::buildTableString([$table]);
|
||||
|
||||
|
@ -1369,7 +1380,8 @@ class Database
|
|||
}
|
||||
|
||||
$sql = "UPDATE " . $ignore . $table_string . " SET "
|
||||
. implode(" = ?, ", array_map([DBA::class, 'quoteIdentifier'], array_keys($fields))) . " = ?"
|
||||
. ((count($fields) > 0) ? implode(" = ?, ", array_map([DBA::class, 'quoteIdentifier'], array_keys($fields))) . " = ?" : "")
|
||||
. ((count($direct_fields) > 0) ? ((count($fields) > 0) ? " , " : "") . implode(" , ", $direct_fields) : "")
|
||||
. $condition_string;
|
||||
|
||||
// Combines the updated fields parameter values with the condition parameter values
|
||||
|
@ -1510,8 +1522,8 @@ class Database
|
|||
|
||||
$result = $this->p($sql, $condition);
|
||||
|
||||
if (($this->driver == self::PDO) && !empty($result) && is_string($table)) {
|
||||
$result->table = $table;
|
||||
if ($this->driver == self::PDO && !empty($result)) {
|
||||
$this->currentTable = $table;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -1758,6 +1770,37 @@ class Database
|
|||
return (['list' => $statelist, 'amount' => $processes]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes tables
|
||||
*
|
||||
* @param string $table a given table
|
||||
*
|
||||
* @return bool True, if successfully optimized, otherwise false
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function optimizeTable(string $table): bool
|
||||
{
|
||||
return $this->e("OPTIMIZE TABLE " . DBA::buildTableString([$table])) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill sleeping database processes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteSleepingProcesses()
|
||||
{
|
||||
$processes = $this->p("SHOW FULL PROCESSLIST");
|
||||
while ($process = $this->fetch($processes)) {
|
||||
if (($process['Command'] != 'Sleep') || ($process['Time'] < 300) || ($process['db'] != $this->databaseName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->e("KILL ?", $process['Id']);
|
||||
}
|
||||
$this->close($processes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a database variable
|
||||
*
|
||||
|
|
|
@ -38,22 +38,24 @@ class DatabaseException extends Exception
|
|||
*
|
||||
* @link https://php.net/manual/en/exception.construct.php
|
||||
*
|
||||
* @param string $message The Database error message.
|
||||
* @param int $code The Database error code.
|
||||
* @param string $query The Database error query.
|
||||
* @param Throwable $previous [optional] The previous throwable used for the exception chaining.
|
||||
* @param string $message The Database error message.
|
||||
* @param int $code The Database error code.
|
||||
* @param string $query The Database error query.
|
||||
* @param Throwable|null $previous [optional] The previous throwable used for the exception chaining.
|
||||
*/
|
||||
public function __construct(string $message, int $code, string $query, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
parent::__construct(sprintf('"%s" at "%s"', $message, $query) , $code, $previous);
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Returns the query, which caused the exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
public function getQuery(): string
|
||||
{
|
||||
return sprintf('Database error %d "%s" at "%s"', $this->message, $this->code, $this->query);
|
||||
return $this->query;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -323,14 +323,19 @@ class Status extends BaseFactory
|
|||
{
|
||||
if (empty($item['quote-uri-id'])) {
|
||||
$media = Post\Media::getByURIId($item['uri-id'], [Post\Media::ACTIVITY]);
|
||||
if (!empty($media) && $shared_item = Post::selectFirst(['uri-id'], ['plink' => $media[0]['url'], 'uid' => [$uid, 0]])) {
|
||||
$quote_id = $shared_item['uri-id'];
|
||||
if (!empty($media)) {
|
||||
if (!empty($media['media-uri-id'])) {
|
||||
$quote_id = $media['media-uri-id'];
|
||||
} else {
|
||||
$shared_item = Post::selectFirst(['uri-id'], ['plink' => $media[0]['url'], 'uid' => [$uid, 0]]);
|
||||
$quote_id = $shared_item['uri-id'] ?? 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$quote_id = $item['quote-uri-id'];
|
||||
}
|
||||
|
||||
if (!empty($quote_id)) {
|
||||
if (!empty($quote_id) && ($quote_id != $item['uri-id'])) {
|
||||
try {
|
||||
$quote = $this->createFromUriId($quote_id, $uid, false, false, false)->toArray();
|
||||
} catch (\Exception $exception) {
|
||||
|
|
|
@ -38,7 +38,7 @@ class StatusSource extends BaseFactory
|
|||
*/
|
||||
public function createFromUriId(int $uriId, int $uid): \Friendica\Object\Api\Mastodon\StatusSource
|
||||
{
|
||||
$post = Post::selectFirst(['uri-id', 'raw-body', 'body', 'title'], ['uri-id' => $uriId, 'uid' => [0, $uid]]);
|
||||
$post = Post::selectOriginal(['uri-id', 'raw-body', 'body', 'title'], ['uri-id' => $uriId, 'uid' => [0, $uid]]);
|
||||
|
||||
$spoiler_text = $post['title'] ?: BBCode::toPlaintext(BBCode::getAbstract($post['body'], Protocol::ACTIVITYPUB));
|
||||
$body = BBCode::toMarkdown(Post\Media::removeFromEndOfBody($post['body']));
|
||||
|
|
|
@ -88,7 +88,10 @@ final class DeliveryQueueItem extends \Friendica\BaseRepository
|
|||
|
||||
public function remove(Entity\DeliveryQueueItem $deliveryQueueItem): bool
|
||||
{
|
||||
return $this->db->delete(self::$table_name, ['uri-id' => $deliveryQueueItem->postUriId, 'gsid' => $deliveryQueueItem->targetServerId]);
|
||||
return $this->db->delete(self::$table_name, [
|
||||
'uri-id' => $deliveryQueueItem->postUriId,
|
||||
'gsid' => $deliveryQueueItem->targetServerId
|
||||
]);
|
||||
}
|
||||
|
||||
public function removeFailedByServerId(int $gsid, int $failedThreshold): bool
|
||||
|
@ -98,16 +101,17 @@ final class DeliveryQueueItem extends \Friendica\BaseRepository
|
|||
|
||||
public function incrementFailed(Entity\DeliveryQueueItem $deliveryQueueItem): bool
|
||||
{
|
||||
return $this->db->e("
|
||||
UPDATE " . DBA::buildTableString([self::$table_name]) . "
|
||||
SET `failed` = `failed` + 1
|
||||
WHERE `uri-id` = ? AND `gsid` = ?",
|
||||
$deliveryQueueItem->postUriId, $deliveryQueueItem->targetServerId
|
||||
);
|
||||
return $this->db->update(self::$table_name, [
|
||||
"`failed` = `failed` + 1"
|
||||
], [
|
||||
"`uri-id` = ? AND `gsid` = ?",
|
||||
$deliveryQueueItem->postUriId,
|
||||
$deliveryQueueItem->targetServerId
|
||||
]);
|
||||
}
|
||||
|
||||
public function optimizeStorage(): bool
|
||||
{
|
||||
return $this->db->e("OPTIMIZE TABLE " . DBA::buildTableString([self::$table_name]));
|
||||
return $this->db->optimizeTable(self::$table_name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -361,7 +361,7 @@ class Contact
|
|||
$background_update = DI::config()->get('system', 'update_active_contacts') ? $contact['local-data'] : true;
|
||||
|
||||
// Update the contact in the background if needed
|
||||
if ($background_update && !self::isLocal($url) && Probe::isProbable($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
if ($background_update && !self::isLocal($url) && Protocol::supportsProbe($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
try {
|
||||
UpdateContact::add(['priority' => Worker::PRIORITY_LOW, 'dont_fork' => true], $contact['id']);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
@ -1152,10 +1152,11 @@ class Contact
|
|||
$status_link = '';
|
||||
$photos_link = '';
|
||||
|
||||
$sparkle = false;
|
||||
if (($contact['network'] === Protocol::DFRN) && !$contact['self'] && empty($contact['pending'])) {
|
||||
$sparkle = true;
|
||||
$profile_link = 'contact/redir/' . $contact['id'];
|
||||
$status_link = $profile_link . '?' . http_build_query(['url' => $contact['url'] . '/status']);
|
||||
$photos_link = $profile_link . '?' . http_build_query(['url' => $contact['url'] . '/photos']);
|
||||
$profile_link = $profile_link . '?' . http_build_query(['url' => $contact['url'] . '/profile']);
|
||||
} else {
|
||||
$profile_link = $contact['url'];
|
||||
}
|
||||
|
@ -1164,12 +1165,6 @@ class Contact
|
|||
$profile_link = '';
|
||||
}
|
||||
|
||||
if ($sparkle) {
|
||||
$status_link = $profile_link . '/status';
|
||||
$photos_link = $profile_link . '/photos';
|
||||
$profile_link = $profile_link . '/profile';
|
||||
}
|
||||
|
||||
if (self::canReceivePrivateMessages($contact) && empty($contact['pending'])) {
|
||||
$pm_url = 'message/new/' . $contact['id'];
|
||||
}
|
||||
|
@ -1279,7 +1274,7 @@ class Contact
|
|||
|
||||
$background_update = DI::config()->get('system', 'update_active_contacts') ? $contact['local-data'] : true;
|
||||
|
||||
if ($background_update && !self::isLocal($url) && Probe::isProbable($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
if ($background_update && !self::isLocal($url) && Protocol::supportsProbe($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
try {
|
||||
UpdateContact::add(['priority' => Worker::PRIORITY_LOW, 'dont_fork' => true], $contact['id']);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
@ -2409,6 +2404,11 @@ class Contact
|
|||
$condition = ['self' => false, 'nurl' => Strings::normaliseLink($url)];
|
||||
|
||||
$condition['network'] = [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB];
|
||||
|
||||
if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT) && Protocol::supportsProbe($contact['network'])) {
|
||||
$condition['network'][] = $contact['network'];
|
||||
}
|
||||
|
||||
self::update($fields, $condition);
|
||||
|
||||
// We mustn't set the update fields for OStatus contacts since they are updated in OnePoll
|
||||
|
@ -2677,6 +2677,8 @@ class Contact
|
|||
return true;
|
||||
}
|
||||
|
||||
$has_local_data = self::hasLocalData($id, $contact);
|
||||
|
||||
$uid = $contact['uid'];
|
||||
unset($contact['uid']);
|
||||
|
||||
|
@ -2697,18 +2699,20 @@ class Contact
|
|||
|
||||
$updated = DateTimeFormat::utcNow();
|
||||
|
||||
$has_local_data = self::hasLocalData($id, $contact);
|
||||
|
||||
if (!Probe::isProbable($ret['network'])) {
|
||||
if (!Protocol::supportsProbe($ret['network']) && !Protocol::supportsProbe($contact['network'])) {
|
||||
// Periodical checks are only done on federated contacts
|
||||
$failed_next_update = null;
|
||||
$success_next_update = null;
|
||||
} elseif ($has_local_data) {
|
||||
$failed_next_update = GServer::getNextUpdateDate(false, $created, $last_update, !in_array($contact['network'], Protocol::FEDERATED));
|
||||
$success_next_update = GServer::getNextUpdateDate(true, $created, $last_update, !in_array($contact['network'], Protocol::FEDERATED));
|
||||
} else {
|
||||
} elseif (in_array($ret['network'], array_merge(Protocol::NATIVE_SUPPORT, [Protocol::ZOT, Protocol::PHANTOM]))) {
|
||||
$failed_next_update = DateTimeFormat::utc('now +6 month');
|
||||
$success_next_update = DateTimeFormat::utc('now +1 month');
|
||||
} else {
|
||||
// We don't check connector networks very often to not run into API rate limits
|
||||
$failed_next_update = DateTimeFormat::utc('now +12 month');
|
||||
$success_next_update = DateTimeFormat::utc('now +12 month');
|
||||
}
|
||||
|
||||
if (Strings::normaliseLink($contact['url']) != Strings::normaliseLink($ret['url'])) {
|
||||
|
@ -3435,7 +3439,7 @@ class Contact
|
|||
*/
|
||||
public static function magicLinkByContact(array $contact, string $url = ''): string
|
||||
{
|
||||
$destination = $url ?: $contact['url']; // Equivalent to ($url != '') ? $url : $contact['url'];
|
||||
$destination = $url ?: $contact['url'];
|
||||
|
||||
if (!DI::userSession()->isAuthenticated()) {
|
||||
return $destination;
|
||||
|
@ -3591,7 +3595,7 @@ class Contact
|
|||
if (empty($contact['id']) && Network::isValidHttpUrl($url)) {
|
||||
Worker::add(Worker::PRIORITY_LOW, 'AddContact', 0, $url);
|
||||
++$added;
|
||||
} elseif (!empty($contact['network']) && Probe::isProbable($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
} elseif (!empty($contact['network']) && Protocol::supportsProbe($contact['network']) && ($contact['next-update'] < DateTimeFormat::utcNow())) {
|
||||
try {
|
||||
UpdateContact::add(['priority' => Worker::PRIORITY_LOW, 'dont_fork' => true], $contact['id']);
|
||||
++$updated;
|
||||
|
|
|
@ -41,6 +41,8 @@ class Conversation
|
|||
const PARCEL_RDF = 12;
|
||||
const PARCEL_RSS = 13;
|
||||
const PARCEL_ATOM = 14;
|
||||
const PARCEL_ATOM03 = 15;
|
||||
const PARCEL_OPML = 16;
|
||||
const PARCEL_TWITTER = 67;
|
||||
const PARCEL_UNKNOWN = 255;
|
||||
|
||||
|
|
|
@ -869,7 +869,7 @@ class GServer
|
|||
}
|
||||
|
||||
// Sanitize incoming data, see https://github.com/friendica/friendica/issues/8565
|
||||
$data['subscribe'] = (bool)$data['subscribe'] ?? false;
|
||||
$data['subscribe'] = (bool)($data['subscribe'] ?? false);
|
||||
|
||||
if (!$data['subscribe'] || empty($data['scope']) || !in_array(strtolower($data['scope']), ['all', 'tags'])) {
|
||||
$data['scope'] = '';
|
||||
|
|
|
@ -392,21 +392,23 @@ class Group
|
|||
/**
|
||||
* Returns the combined list of contact ids from a group id list
|
||||
*
|
||||
* @param int $uid User id
|
||||
* @param array $group_ids Groups ids
|
||||
* @param boolean $check_dead Whether check "dead" records (?)
|
||||
* @param int $uid User id
|
||||
* @param array $group_ids Groups ids
|
||||
* @param boolean $check_dead Whether check "dead" records (?)
|
||||
* @param boolean $expand_followers Expand the list of followers
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function expand(int $uid, array $group_ids, bool $check_dead = false): array
|
||||
public static function expand(int $uid, array $group_ids, bool $check_dead = false, bool $expand_followers = true): array
|
||||
{
|
||||
if (!is_array($group_ids) || !count($group_ids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$return = [];
|
||||
$pubmail = false;
|
||||
$networks = Protocol::SUPPORT_PRIVATE;
|
||||
$return = [];
|
||||
$pubmail = false;
|
||||
$followers_collection = false;
|
||||
$networks = Protocol::SUPPORT_PRIVATE;
|
||||
|
||||
$mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', $uid]);
|
||||
if (DBA::isResult($mailacct)) {
|
||||
|
@ -419,20 +421,23 @@ class Group
|
|||
|
||||
$key = array_search(self::FOLLOWERS, $group_ids);
|
||||
if ($key !== false) {
|
||||
$followers = Contact::selectToArray(['id'], [
|
||||
'uid' => $uid,
|
||||
'rel' => [Contact::FOLLOWER, Contact::FRIEND],
|
||||
'network' => $networks,
|
||||
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON],
|
||||
'archive' => false,
|
||||
'pending' => false,
|
||||
'blocked' => false,
|
||||
]);
|
||||
if ($expand_followers) {
|
||||
$followers = Contact::selectToArray(['id'], [
|
||||
'uid' => $uid,
|
||||
'rel' => [Contact::FOLLOWER, Contact::FRIEND],
|
||||
'network' => $networks,
|
||||
'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON],
|
||||
'archive' => false,
|
||||
'pending' => false,
|
||||
'blocked' => false,
|
||||
]);
|
||||
|
||||
foreach ($followers as $follower) {
|
||||
$return[] = $follower['id'];
|
||||
foreach ($followers as $follower) {
|
||||
$return[] = $follower['id'];
|
||||
}
|
||||
} else {
|
||||
$followers_collection = true;
|
||||
}
|
||||
|
||||
unset($group_ids[$key]);
|
||||
}
|
||||
|
||||
|
@ -465,6 +470,10 @@ class Group
|
|||
$return = Contact::pruneUnavailable($return);
|
||||
}
|
||||
|
||||
if ($followers_collection) {
|
||||
$return[] = -1;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ class Item
|
|||
const PR_DISTRIBUTE = 79;
|
||||
const PR_PUSHED = 80;
|
||||
const PR_LOCAL = 81;
|
||||
const PR_AUDIENCE = 82;
|
||||
|
||||
// system.accept_only_sharer setting values
|
||||
const COMPLETION_NONE = 1;
|
||||
|
@ -1624,7 +1625,7 @@ class Item
|
|||
|
||||
if (($uid != 0) && (($item['gravity'] == self::GRAVITY_PARENT) || $is_reshare) &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE &&
|
||||
!in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY])) {
|
||||
!in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY, self::PR_AUDIENCE])) {
|
||||
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid, 'uri-id' => $uri_id, 'post-reason' => $item['post-reason']]);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2509,17 +2510,22 @@ class Item
|
|||
/**
|
||||
* Returns an array of contact-ids that are allowed to see this object
|
||||
*
|
||||
* @param array $obj Item array with at least uid, allow_cid, allow_gid, deny_cid and deny_gid
|
||||
* @param bool $check_dead Prunes unavailable contacts from the result
|
||||
* @param array $obj Item array with at least uid, allow_cid, allow_gid, deny_cid and deny_gid
|
||||
* @param bool $check_dead Prunes unavailable contacts from the result
|
||||
* @param bool $expand_followers Expand the list of followers
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function enumeratePermissions(array $obj, bool $check_dead = false): array
|
||||
public static function enumeratePermissions(array $obj, bool $check_dead = false, bool $expand_followers = true): array
|
||||
{
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
||||
if (!$expand_followers && (!empty($obj['deny_cid']) || !empty($obj['deny_gid']))) {
|
||||
$expand_followers = true;
|
||||
}
|
||||
|
||||
$allow_people = $aclFormatter->expand($obj['allow_cid']);
|
||||
$allow_groups = Group::expand($obj['uid'], $aclFormatter->expand($obj['allow_gid']), $check_dead);
|
||||
$allow_groups = Group::expand($obj['uid'], $aclFormatter->expand($obj['allow_gid']), $check_dead, $expand_followers);
|
||||
$deny_people = $aclFormatter->expand($obj['deny_cid']);
|
||||
$deny_groups = Group::expand($obj['uid'], $aclFormatter->expand($obj['deny_gid']), $check_dead);
|
||||
$recipients = array_unique(array_merge($allow_people, $allow_groups));
|
||||
|
@ -3015,6 +3021,7 @@ class Item
|
|||
if (!$is_preview) {
|
||||
$item['body'] = preg_replace("#\s*\[attachment .*?].*?\[/attachment]\s*#ism", "\n", $item['body']);
|
||||
$item['body'] = Post\Media::removeFromEndOfBody($item['body'] ?? '');
|
||||
$item['body'] = Post\Media::replaceImage($item['body']);
|
||||
}
|
||||
|
||||
$body = $item['body'];
|
||||
|
@ -3032,13 +3039,19 @@ class Item
|
|||
if (!empty($shared['post'])) {
|
||||
$shared_item = $shared['post'];
|
||||
$shared_item['body'] = Post\Media::removeFromEndOfBody($shared_item['body']);
|
||||
$shared_item['body'] = Post\Media::replaceImage($shared_item['body']);
|
||||
$quote_uri_id = $shared['post']['uri-id'];
|
||||
$shared_links[] = strtolower($shared['post']['uri']);
|
||||
$item['body'] = BBCode::removeSharedData($item['body']);
|
||||
} elseif (empty($shared_item['uri-id']) && empty($item['quote-uri-id']) && ($item['network'] != Protocol::DIASPORA)) {
|
||||
$media = Post\Media::getByURIId($item['uri-id'], [Post\Media::ACTIVITY]);
|
||||
if (!empty($media)) {
|
||||
$shared_item = Post::selectFirst($fields, ['plink' => $media[0]['url'], 'uid' => [$item['uid'], 0]]);
|
||||
$shared_item = Post::selectFirst($fields, ['uri-id' => $media[0]['media-uri-id'], 'uid' => [$item['uid'], 0]]);
|
||||
if (empty($shared_item['uri-id'])) {
|
||||
$shared_item = Post::selectFirst($fields, ['plink' => $media[0]['url'], 'uid' => [$item['uid'], 0]]);
|
||||
} elseif (!in_array(strtolower($media[0]['url']), $shared_links)) {
|
||||
$shared_links[] = strtolower($media[0]['url']);
|
||||
}
|
||||
|
||||
if (empty($shared_item['uri-id'])) {
|
||||
$shared_item = Post::selectFirst($fields, ['uri' => $media[0]['url'], 'uid' => [$item['uid'], 0]]);
|
||||
|
@ -3130,12 +3143,14 @@ class Item
|
|||
}
|
||||
|
||||
if (!empty($shared_attachments)) {
|
||||
$s = self::addGallery($s, $shared_attachments, $item['uri-id']);
|
||||
$s = self::addVisualAttachments($shared_attachments, $shared_item, $s, true);
|
||||
$s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $shared_attachments, $body, $s, true, $quote_shared_links);
|
||||
$s = self::addNonVisualAttachments($shared_attachments, $item, $s, true);
|
||||
$body = BBCode::removeSharedData($body);
|
||||
}
|
||||
|
||||
$s = self::addGallery($s, $attachments, $item['uri-id']);
|
||||
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
||||
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
|
||||
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
||||
|
@ -3185,6 +3200,24 @@ class Item
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify links to pictures to links for the "Fancybox" gallery
|
||||
*
|
||||
* @param string $s
|
||||
* @param array $attachments
|
||||
* @param integer $uri_id
|
||||
* @return string
|
||||
*/
|
||||
private static function addGallery(string $s, array $attachments, int $uri_id): string
|
||||
{
|
||||
foreach ($attachments['visual'] as $attachment) {
|
||||
if (empty($attachment['preview']) || ($attachment['type'] != Post\Media::IMAGE)) {
|
||||
continue;
|
||||
}
|
||||
$s = str_replace('<a href="' . $attachment['url'] . '"', '<a data-fancybox="' . $uri_id . '" href="' . $attachment['url'] . '"', $s);
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the body contains a link
|
||||
|
@ -3248,12 +3281,18 @@ class Item
|
|||
|
||||
foreach ($attachments['visual'] as $attachment) {
|
||||
if (!empty($attachment['preview'])) {
|
||||
if (Network::isLocalLink($attachment['preview'])) {
|
||||
continue;
|
||||
}
|
||||
$proxy = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE);
|
||||
$search = ['[img=' . $attachment['preview'] . ']', ']' . $attachment['preview'] . '[/img]'];
|
||||
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
|
||||
|
||||
$body = str_replace($search, $replace, $body);
|
||||
} elseif ($attachment['filetype'] == 'image') {
|
||||
if (Network::isLocalLink($attachment['url'])) {
|
||||
continue;
|
||||
}
|
||||
$proxy = Post\Media::getUrlForId($attachment['id']);
|
||||
$search = ['[img=' . $attachment['url'] . ']', ']' . $attachment['url'] . '[/img]'];
|
||||
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
|
||||
|
@ -3333,7 +3372,7 @@ class Item
|
|||
if (self::containsLink($item['body'], $src_url)) {
|
||||
continue;
|
||||
}
|
||||
$images[] = ['src' => $src_url, 'preview' => $preview_url, 'attachment' => $attachment];
|
||||
$images[] = ['src' => $src_url, 'preview' => $preview_url, 'attachment' => $attachment, 'uri_id' => $item['uri-id']];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -918,9 +918,7 @@ class Photo
|
|||
*/
|
||||
public static function getResourceData(string $name): array
|
||||
{
|
||||
$base = DI::baseUrl();
|
||||
|
||||
$guid = str_replace([Strings::normaliseLink($base), '/photo/'], '', Strings::normaliseLink($name));
|
||||
$guid = str_replace([Strings::normaliseLink((string)DI::baseUrl()), '/photo/'], '', Strings::normaliseLink($name));
|
||||
|
||||
if (parse_url($guid, PHP_URL_SCHEME)) {
|
||||
return [];
|
||||
|
@ -982,9 +980,7 @@ class Photo
|
|||
*/
|
||||
public static function isLocalPage(string $name): bool
|
||||
{
|
||||
$base = DI::baseUrl();
|
||||
|
||||
$guid = str_replace(Strings::normaliseLink($base), '', Strings::normaliseLink($name));
|
||||
$guid = str_replace(Strings::normaliseLink((string)DI::baseUrl()), '', Strings::normaliseLink($name));
|
||||
$guid = preg_replace("=/photos/.*/image/(.*)=ism", '$1', $guid);
|
||||
if (empty($guid)) {
|
||||
return false;
|
||||
|
@ -1148,7 +1144,7 @@ class Photo
|
|||
return [];
|
||||
}
|
||||
|
||||
return ['image' => $image, 'filename' => $filename];
|
||||
return ['image' => $image, 'filename' => $filename, 'size' => $filesize];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1182,8 +1178,7 @@ class Photo
|
|||
|
||||
$image = $data['image'];
|
||||
$filename = $data['filename'];
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
$filesize = $data['size'];
|
||||
|
||||
$resource_id = $resource_id ?: self::newResource();
|
||||
$album = $album ?: DI::l10n()->t('Wall Photos');
|
||||
|
@ -1193,30 +1188,12 @@ class Photo
|
|||
$allow_gid = '';
|
||||
}
|
||||
|
||||
$smallest = 0;
|
||||
|
||||
$r = self::store($image, $user['uid'], 0, $resource_id, $filename, $album, 0, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
|
||||
if (!$r) {
|
||||
$preview = self::storeWithPreview($image, $user['uid'], $resource_id, $filename, $filesize, $album, $desc, $allow_cid, $allow_gid, $deny_cid, $deny_gid);
|
||||
if ($preview < 0) {
|
||||
Logger::warning('Photo could not be stored', ['uid' => $user['uid'], 'resource_id' => $resource_id, 'filename' => $filename, 'album' => $album]);
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($width > 640 || $height > 640) {
|
||||
$image->scaleDown(640);
|
||||
$r = self::store($image, $user['uid'], 0, $resource_id, $filename, $album, 1, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
|
||||
if ($r) {
|
||||
$smallest = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($width > 320 || $height > 320) {
|
||||
$image->scaleDown(320);
|
||||
$r = self::store($image, $user['uid'], 0, $resource_id, $filename, $album, 2, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
|
||||
if ($r && ($smallest == 0)) {
|
||||
$smallest = 2;
|
||||
}
|
||||
}
|
||||
|
||||
$condition = ['resource-id' => $resource_id];
|
||||
$photo = self::selectFirst(['id', 'datasize', 'width', 'height', 'type'], $condition, ['order' => ['width' => true]]);
|
||||
if (empty($photo)) {
|
||||
|
@ -1234,12 +1211,87 @@ class Photo
|
|||
$picture['type'] = $photo['type'];
|
||||
$picture['albumpage'] = DI::baseUrl() . '/photos/' . $user['nickname'] . '/image/' . $resource_id;
|
||||
$picture['picture'] = DI::baseUrl() . '/photo/' . $resource_id . '-0.' . $image->getExt();
|
||||
$picture['preview'] = DI::baseUrl() . '/photo/' . $resource_id . '-' . $smallest . '.' . $image->getExt();
|
||||
$picture['preview'] = DI::baseUrl() . '/photo/' . $resource_id . '-' . $preview . '.' . $image->getExt();
|
||||
|
||||
Logger::info('upload done', ['picture' => $picture]);
|
||||
return $picture;
|
||||
}
|
||||
|
||||
/**
|
||||
* store photo metadata in db and binary with preview photos in default backend
|
||||
*
|
||||
* @param Image $image Image object with data
|
||||
* @param integer $uid User ID
|
||||
* @param string $resource_id Resource ID
|
||||
* @param string $filename Filename
|
||||
* @param integer $filesize Filesize
|
||||
* @param string $album Album name
|
||||
* @param string $description Photo caption
|
||||
* @param string $allow_cid Permissions, allowed contacts
|
||||
* @param string $allow_gid Permissions, allowed groups
|
||||
* @param string $deny_cid Permissions, denied contacts
|
||||
* @param string $deny_gid Permissions, denied group
|
||||
*
|
||||
* @return integer preview photo size
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function storeWithPreview(Image $image, int $uid, string $resource_id, string $filename, int $filesize, string $album, string $description, string $allow_cid, string $allow_gid, string $deny_cid, string $deny_gid): int
|
||||
{
|
||||
if ($filesize == 0) {
|
||||
$filesize = strlen($image->asString());
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));
|
||||
|
||||
if ($maximagesize && $filesize > $maximagesize) {
|
||||
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
|
||||
foreach ([5120, 2560, 1280, 640, 320] as $pixels) {
|
||||
if ($filesize > $maximagesize && max($width, $height) > $pixels) {
|
||||
DI::logger()->info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
if ($filesize > $maximagesize) {
|
||||
DI::logger()->notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
$preview = 0;
|
||||
|
||||
$result = self::store($image, $uid, 0, $resource_id, $filename, $album, 0, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $description);
|
||||
if (!$result) {
|
||||
Logger::warning('Photo could not be stored', ['uid' => $uid, 'resource_id' => $resource_id, 'filename' => $filename, 'album' => $album]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($width > 640 || $height > 640) {
|
||||
$image->scaleDown(640);
|
||||
}
|
||||
|
||||
if ($width > 320 || $height > 320) {
|
||||
$result = self::store($image, $uid, 0, $resource_id, $filename, $album, 1, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $description);
|
||||
if ($result) {
|
||||
$preview = 1;
|
||||
}
|
||||
$image->scaleDown(320);
|
||||
$result = self::store($image, $uid, 0, $resource_id, $filename, $album, 2, self::DEFAULT, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $description);
|
||||
if ($result && ($preview == 0)) {
|
||||
$preview = 2;
|
||||
}
|
||||
}
|
||||
return $preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a user avatar
|
||||
*
|
||||
|
|
|
@ -226,6 +226,46 @@ class Post
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single record from the post-user-view view and returns it in an associative array
|
||||
* When the requested record is a reshare activity, the system fetches the reshared original post.
|
||||
* Otherwise the function reacts similar to selectFirst
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $condition
|
||||
* @param array $params
|
||||
* @param bool $user_mode true = post-user-view, false = post-view
|
||||
* @return bool|array
|
||||
* @throws \Exception
|
||||
* @see DBA::select
|
||||
*/
|
||||
public static function selectOriginal(array $fields = [], array $condition = [], array $params = [])
|
||||
{
|
||||
$original_fields = $fields;
|
||||
$remove = [];
|
||||
if (!empty($fields)) {
|
||||
foreach (['gravity', 'verb', 'thr-parent-id', 'uid'] as $field) {
|
||||
if (!in_array($field, $fields)) {
|
||||
$fields[] = $field;
|
||||
$remove[] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = self::selectFirst($fields, $condition, $params);
|
||||
if (empty($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (($result['gravity'] != Item::GRAVITY_ACTIVITY) || ($result['verb'] != Activity::ANNOUNCE)) {
|
||||
foreach ($remove as $field) {
|
||||
unset($result[$field]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return self::selectFirst($original_fields, ['uri-id' => $result['thr-parent-id'], 'uid' => [0, $result['uid']]], $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single record from the post-view view and returns it in an associative array
|
||||
*
|
||||
|
@ -505,6 +545,46 @@ class Post
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single record from the post-user-view view for a given user and returns it in an associative array
|
||||
* When the requested record is a reshare activity, the system fetches the reshared original post.
|
||||
* Otherwise the function reacts similar to selectFirstForUser
|
||||
*
|
||||
* @param integer $uid User ID
|
||||
* @param array $selected
|
||||
* @param array $condition
|
||||
* @param array $params
|
||||
* @return bool|array
|
||||
* @throws \Exception
|
||||
* @see DBA::select
|
||||
*/
|
||||
public static function selectOriginalForUser(int $uid, array $selected = [], array $condition = [], array $params = [])
|
||||
{
|
||||
$original_selected = $selected;
|
||||
$remove = [];
|
||||
if (!empty($selected)) {
|
||||
foreach (['gravity', 'verb', 'thr-parent-id'] as $field) {
|
||||
if (!in_array($field, $selected)) {
|
||||
$selected[] = $field;
|
||||
$remove[] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = self::selectFirstForUser($uid, $selected, $condition, $params);
|
||||
if (empty($result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (($result['gravity'] != Item::GRAVITY_ACTIVITY) || ($result['verb'] != Activity::ANNOUNCE)) {
|
||||
foreach ($remove as $field) {
|
||||
unset($result[$field]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return self::selectFirstForUser($uid, $original_selected, ['uri-id' => $result['thr-parent-id'], 'uid' => [0, $uid]], $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing post entries
|
||||
*
|
||||
|
|
|
@ -78,7 +78,7 @@ class Delivery
|
|||
*/
|
||||
public static function incrementFailed(int $uri_id, string $inbox)
|
||||
{
|
||||
return DBA::e('UPDATE `post-delivery` SET `failed` = `failed` + 1 WHERE `uri-id` = ? AND `inbox-id` = ?', $uri_id, ItemURI::getIdByURI($inbox));
|
||||
return DBA::update('post-delivery', ["`failed` = `failed` + 1"], ['uri-id' => $uri_id, 'inbox-id' => ItemURI::getIdByURI($inbox)]);
|
||||
}
|
||||
|
||||
public static function selectForInbox(string $inbox)
|
||||
|
|
|
@ -82,27 +82,27 @@ class DeliveryData
|
|||
*/
|
||||
public static function incrementQueueDone(int $uri_id, int $protocol = 0)
|
||||
{
|
||||
$sql = '';
|
||||
$increments = ["`queue_done` = `queue_done` + 1"];
|
||||
|
||||
switch ($protocol) {
|
||||
case self::ACTIVITYPUB:
|
||||
$sql = ", `activitypub` = `activitypub` + 1";
|
||||
$increments[] = "`activitypub` = `activitypub` + 1";
|
||||
break;
|
||||
case self::DFRN:
|
||||
$sql = ", `dfrn` = `dfrn` + 1";
|
||||
$increments[] = "`dfrn` = `dfrn` + 1";
|
||||
break;
|
||||
case self::LEGACY_DFRN:
|
||||
$sql = ", `legacy_dfrn` = `legacy_dfrn` + 1";
|
||||
$increments[] = "`legacy_dfrn` = `legacy_dfrn` + 1";
|
||||
break;
|
||||
case self::DIASPORA:
|
||||
$sql = ", `diaspora` = `diaspora` + 1";
|
||||
$increments[] = "`diaspora` = `diaspora` + 1";
|
||||
break;
|
||||
case self::OSTATUS:
|
||||
$sql = ", `ostatus` = `ostatus` + 1";
|
||||
$increments[] = "`ostatus` = `ostatus` + 1";
|
||||
break;
|
||||
}
|
||||
|
||||
return DBA::e('UPDATE `post-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `uri-id` = ?', $uri_id);
|
||||
return DBA::update('post-delivery-data', $increments, ['uri-id' => $uri_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,7 +116,7 @@ class DeliveryData
|
|||
*/
|
||||
public static function incrementQueueFailed(int $uri_id)
|
||||
{
|
||||
return DBA::e('UPDATE `post-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `uri-id` = ?', $uri_id);
|
||||
return DBA::update('post-delivery-data', ["`queue_failed` = `queue_failed` + 1"], ['uri-id' => $uri_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +129,7 @@ class DeliveryData
|
|||
*/
|
||||
public static function incrementQueueCount(int $uri_id, int $increment = 1)
|
||||
{
|
||||
return DBA::e('UPDATE `post-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `uri-id` = ?', $increment, $uri_id);
|
||||
return DBA::update('post-delivery-data', ["`queue_count` = `queue_count` + $increment"], ['uri-id' => $uri_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -463,7 +463,7 @@ class Media
|
|||
*/
|
||||
private static function isLinkToPhoto(string $page, string $preview): bool
|
||||
{
|
||||
return preg_match('#/photo/.*-0\.#ism', $page) && preg_match('#/photo/.*-[01]\.#ism', $preview);
|
||||
return preg_match('#/photo/.*-0\.#ism', $page) && preg_match('#/photo/.*-[012]\.#ism', $preview);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -475,7 +475,34 @@ class Media
|
|||
*/
|
||||
private static function isLinkToImagePage(string $page, string $preview): bool
|
||||
{
|
||||
return preg_match('#/photos/.*/image/#ism', $page) && preg_match('#/photo/.*-[01]\.#ism', $preview);
|
||||
return preg_match('#/photos/.*/image/#ism', $page) && preg_match('#/photo/.*-[012]\.#ism', $preview);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the image link in Friendica image posts with a link to the image
|
||||
*
|
||||
* @param string $body
|
||||
* @return string
|
||||
*/
|
||||
public static function replaceImage(string $body): string
|
||||
{
|
||||
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], Images::getBBCodeByUrl(str_replace(['-1.', '-2.'], '-0.', $picture[2]), $picture[2], $picture[3]), $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], Images::getBBCodeByUrl(str_replace(['-1.', '-2.'], '-0.', $picture[2]), $picture[2]), $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -498,7 +525,7 @@ class Media
|
|||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$image = str_replace('-1.', '-0.', $picture[2]);
|
||||
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
|
||||
$attachments[$image] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
|
||||
'preview' => $picture[2], 'description' => $picture[3]
|
||||
|
@ -530,7 +557,7 @@ class Media
|
|||
foreach ($pictures as $picture) {
|
||||
if (self::isLinkToImagePage($picture[1], $picture[2])) {
|
||||
$body = str_replace($picture[0], '', $body);
|
||||
$image = str_replace('-1.', '-0.', $picture[2]);
|
||||
$image = str_replace(['-1.', '-2.'], '-0.', $picture[2]);
|
||||
$attachments[$image] = [
|
||||
'uri-id' => $uriid, 'type' => self::IMAGE, 'url' => $image,
|
||||
'preview' => $picture[2], 'description' => null
|
||||
|
@ -977,19 +1004,7 @@ class Media
|
|||
}
|
||||
|
||||
if ($media['type'] == self::IMAGE) {
|
||||
if (!empty($media['preview'])) {
|
||||
if (!empty($media['description'])) {
|
||||
$body .= "\n[url=" . $media['url'] . "][img=" . $media['preview'] . ']' . $media['description'] . '[/img][/url]';
|
||||
} else {
|
||||
$body .= "\n[url=" . $media['url'] . "][img]" . $media['preview'] . '[/img][/url]';
|
||||
}
|
||||
} else {
|
||||
if (!empty($media['description'])) {
|
||||
$body .= "\n[img=" . $media['url'] . ']' . $media['description'] . '[/img]';
|
||||
} else {
|
||||
$body .= "\n[img]" . $media['url'] . '[/img]';
|
||||
}
|
||||
}
|
||||
$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) {
|
||||
|
|
|
@ -54,10 +54,12 @@ class Tag
|
|||
*/
|
||||
const EXCLUSIVE_MENTION = 9;
|
||||
|
||||
const TO = 10;
|
||||
const CC = 11;
|
||||
const BTO = 12;
|
||||
const BCC = 13;
|
||||
const TO = 10;
|
||||
const CC = 11;
|
||||
const BTO = 12;
|
||||
const BCC = 13;
|
||||
const AUDIENCE = 14;
|
||||
const ATTRIBUTED = 15;
|
||||
|
||||
const ACCOUNT = 1;
|
||||
const GENERAL_COLLECTION = 2;
|
||||
|
@ -103,7 +105,7 @@ class Tag
|
|||
$cid = 0;
|
||||
$tagid = 0;
|
||||
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC, self::AUDIENCE, self::ATTRIBUTED])) {
|
||||
if (empty($url)) {
|
||||
// No mention without a contact url
|
||||
return;
|
||||
|
@ -130,7 +132,7 @@ class Tag
|
|||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
if (!in_array($type, [self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (!in_array($type, [self::TO, self::CC, self::BTO, self::BCC, self::AUDIENCE, self::ATTRIBUTED])) {
|
||||
if (($type != self::HASHTAG) && !empty($url) && ($url != $name)) {
|
||||
$url = strtolower($url);
|
||||
} else {
|
||||
|
|
|
@ -167,7 +167,7 @@ class User
|
|||
$system['region'] = '';
|
||||
$system['postal-code'] = '';
|
||||
$system['country-name'] = '';
|
||||
$system['homepage'] = DI::baseUrl();
|
||||
$system['homepage'] = (string)DI::baseUrl();
|
||||
$system['dob'] = '0000-00-00';
|
||||
|
||||
// Ensure that the user contains data
|
||||
|
|
|
@ -35,6 +35,7 @@ use Friendica\Module\Register;
|
|||
use Friendica\Object\Api\Mastodon\InstanceV2 as InstanceEntity;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
@ -112,7 +113,7 @@ class InstanceV2 extends BaseApi
|
|||
$this->config->get('config', 'max_import_size')
|
||||
));
|
||||
|
||||
$image_size_limit = $this->config->get('system', 'maximagesize');
|
||||
$image_size_limit = Strings::getBytesFromShorthand($this->config->get('system', 'maximagesize'));
|
||||
|
||||
return new InstanceEntity\Configuration(
|
||||
$statuses_config,
|
||||
|
|
|
@ -28,7 +28,6 @@ use Friendica\Model\Contact;
|
|||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Navigation\Notifications\Entity;
|
||||
use Friendica\Object\Api\Mastodon\Notification;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
||||
|
@ -59,7 +58,7 @@ class Notifications extends BaseApi
|
|||
'max_id' => 0, // Return results older than this ID
|
||||
'since_id' => 0, // Return results newer than this ID
|
||||
'min_id' => 0, // Return results immediately newer than this ID
|
||||
'limit' => 20, // Maximum number of results to return (default 20)
|
||||
'limit' => 15, // Maximum number of results to return. Defaults to 15 notifications. Max 30 notifications.
|
||||
'exclude_types' => [], // Array of types to exclude (follow, favourite, reblog, mention, poll, follow_request)
|
||||
'account_id' => 0, // Return only notifications received from this account
|
||||
'with_muted' => false, // Pleroma extension: return activities by muted (not by blocked!) users.
|
||||
|
@ -142,7 +141,7 @@ class Notifications extends BaseApi
|
|||
$params,
|
||||
$request['min_id'] ?: $request['since_id'],
|
||||
$request['max_id'],
|
||||
$request['limit']
|
||||
min($request['limit'], 30)
|
||||
);
|
||||
|
||||
foreach ($Notifications as $Notification) {
|
||||
|
|
|
@ -268,7 +268,7 @@ class Statuses extends BaseApi
|
|||
}
|
||||
|
||||
if ($request['in_reply_to_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_id'], 'uid' => [0, $uid]]);
|
||||
$parent = Post::selectOriginal(['uri'], ['uri-id' => $request['in_reply_to_id'], 'uid' => [0, $uid]]);
|
||||
if (empty($parent)) {
|
||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $request['in_reply_to_id'] . ' not found for user ' . $uid . '.');
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class Bookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
$item = Post::selectOriginal(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class Bookmark extends BaseApi
|
|||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid, ['post-reason' => Item::PR_ACTIVITY]);
|
||||
$stored = Item::storeForUserByUriId($item['id'], $uid, ['post-reason' => Item::PR_ACTIVITY]);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
|
|
|
@ -43,13 +43,11 @@ class Card extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$id = $this->parameters['id'];
|
||||
|
||||
if (!Post::exists(['uri-id' => $id, 'uid' => [0, $uid]])) {
|
||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $id . ' not found' . ($uid ? ' for user ' . $uid : '.'));
|
||||
if (!$post = Post::selectOriginal(['id'], ['uri-id' => $this->parameters['id'], 'uid' => [0, $uid]])) {
|
||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found' . ($uid ? ' for user ' . $uid : '.'));
|
||||
}
|
||||
|
||||
$card = DI::mstdnCard()->createFromUriId($id);
|
||||
$card = DI::mstdnCard()->createFromUriId($post['id']);
|
||||
|
||||
System::jsonExit($card->toArray());
|
||||
}
|
||||
|
|
|
@ -57,8 +57,9 @@ class Context extends BaseApi
|
|||
$parents = [];
|
||||
$children = [];
|
||||
|
||||
$parent = Post::selectFirst(['parent-uri-id'], ['uri-id' => $id]);
|
||||
$parent = Post::selectOriginal(['uri-id', 'parent-uri-id'], ['uri-id' => $id]);
|
||||
if (DBA::isResult($parent)) {
|
||||
$id = $parent['uri-id'];
|
||||
$params = ['order' => ['uri-id' => true]];
|
||||
$condition = ['parent-uri-id' => $parent['parent-uri-id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]];
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class Favourite extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
|
@ -44,12 +44,11 @@ class FavouritedBy extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$id = $this->parameters['id'];
|
||||
if (!Post::exists(['uri-id' => $id, 'uid' => [0, $uid]])) {
|
||||
if (!$post = Post::selectOriginal(['id'], ['uri-id' => $this->parameters['id'], 'uid' => [0, $uid]])) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
$activities = Post::selectPosts(['author-id'], ['thr-parent-id' => $id, 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::LIKE, 'deleted' => false]);
|
||||
$activities = Post::selectPosts(['author-id'], ['thr-parent-id' => $post['id'], 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::LIKE, 'deleted' => false]);
|
||||
|
||||
$accounts = [];
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class Mute extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class Mute extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be muted'));
|
||||
}
|
||||
|
||||
Post\ThreadUser::setIgnored($this->parameters['id'], $uid, true);
|
||||
Post\ThreadUser::setIgnored($item['id'], $uid, true);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid, self::appSupportsQuotes())->toArray());
|
||||
}
|
||||
|
|
|
@ -41,12 +41,12 @@ class Pin extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity', 'author-id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'gravity', 'author-id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED, $item['author-id'], $uid);
|
||||
Post\Collection::add($item['id'], Post\Collection::FEATURED, $item['author-id'], $uid);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid, self::appSupportsQuotes())->toArray());
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class Reblog extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'network'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'network'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
|
@ -44,12 +44,11 @@ class RebloggedBy extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$id = $this->parameters['id'];
|
||||
if (!Post::exists(['uri-id' => $id, 'uid' => [0, $uid]])) {
|
||||
if (!$post = Post::selectOriginal(['id'], ['uri-id' => $this->parameters['id'], 'uid' => [0, $uid]])) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
$activities = Post::selectPosts(['author-id'], ['thr-parent-id' => $id, 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE]);
|
||||
$activities = Post::selectPosts(['author-id'], ['thr-parent-id' => $post['id'], 'gravity' => Item::GRAVITY_ACTIVITY, 'verb' => Activity::ANNOUNCE]);
|
||||
|
||||
$accounts = [];
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class Unbookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
$item = Post::selectOriginal(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class Unbookmark extends BaseApi
|
|||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid, ['post-reason' => Item::PR_ACTIVITY]);
|
||||
$stored = Item::storeForUserByUriId($item['id'], $uid, ['post-reason' => Item::PR_ACTIVITY]);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
|
|
|
@ -42,7 +42,7 @@ class Unfavourite extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class Unmute extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class Unmute extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be unmuted'));
|
||||
}
|
||||
|
||||
Post\ThreadUser::setIgnored($this->parameters['id'], $uid, false);
|
||||
Post\ThreadUser::setIgnored($item['id'], $uid, false);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid, self::appSupportsQuotes())->toArray());
|
||||
}
|
||||
|
|
|
@ -41,12 +41,12 @@ class Unpin extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED, $uid);
|
||||
Post\Collection::remove($item['id'], Post\Collection::FEATURED, $uid);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid, self::appSupportsQuotes())->toArray());
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class Unreblog extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'network'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectOriginalForUser($uid, ['id', 'network'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module;
|
|||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Content\Pager;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\DI;
|
||||
|
@ -62,18 +63,13 @@ class BaseSearch extends BaseModule
|
|||
}
|
||||
|
||||
$header = '';
|
||||
$results = new ResultList();
|
||||
|
||||
if (strpos($search, '@') === 0) {
|
||||
$search = trim(substr($search, 1));
|
||||
$type = Search::TYPE_PEOPLE;
|
||||
$header = DI::l10n()->t('People Search - %s', $search);
|
||||
|
||||
if (strrpos($search, '@') > 0) {
|
||||
$results = Search::getContactsFromProbe(Network::convertToIdn($search));
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($search, '!') === 0) {
|
||||
} elseif (strpos($search, '!') === 0) {
|
||||
$search = trim(substr($search, 1));
|
||||
$type = Search::TYPE_FORUM;
|
||||
$header = DI::l10n()->t('Forum Search - %s', $search);
|
||||
|
@ -91,14 +87,18 @@ class BaseSearch extends BaseModule
|
|||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
|
||||
|
||||
if ($localSearch && empty($results)) {
|
||||
$pager->setItemsPerPage(80);
|
||||
$results = Search::getContactsFromLocalDirectory($search, $type, $pager->getStart(), $pager->getItemsPerPage());
|
||||
} elseif (Search::getGlobalDirectory() && empty($results)) {
|
||||
if (!$results->getTotal() && !$localSearch && Search::getGlobalDirectory()) {
|
||||
$results = Search::getContactsFromGlobalDirectory($search, $type, $pager->getPage());
|
||||
$pager->setItemsPerPage($results->getItemsPage());
|
||||
} else {
|
||||
$results = new ResultList();
|
||||
}
|
||||
|
||||
if (!$results->getTotal()) {
|
||||
$pager->setItemsPerPage(80);
|
||||
$results = Search::getContactsFromLocalDirectory($search, $type, $pager->getStart(), $pager->getItemsPerPage());
|
||||
}
|
||||
|
||||
if (!$results->getTotal()) {
|
||||
$results = Search::getContactsFromProbe(Network::convertToIdn($search), $type == Search::TYPE_FORUM);
|
||||
}
|
||||
|
||||
return self::printResult($results, $pager, $header);
|
||||
|
@ -151,4 +151,4 @@ class BaseSearch extends BaseModule
|
|||
'$paginate' => $pager->renderFull($results->getTotal()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -463,7 +463,7 @@ class Profile extends BaseModule
|
|||
];
|
||||
}
|
||||
|
||||
if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
|
||||
if (Protocol::supportsProbe($contact['network'])) {
|
||||
$contact_actions['updateprofile'] = [
|
||||
'label' => $this->t('Refetch contact data'),
|
||||
'url' => 'contact/' . $contact['id'] . '/updateprofile?t=' . $formSecurityToken,
|
||||
|
|
|
@ -64,9 +64,9 @@ class HCard extends BaseModule
|
|||
$page['htmlhead'] .= '<link rel="openid.delegate" href="' . $delegate . '" />' . "\r\n";
|
||||
}
|
||||
|
||||
$baseUrl = DI::baseUrl();
|
||||
$baseUrl = (string)DI::baseUrl();
|
||||
|
||||
$uri = urlencode('acct:' . $profile['nickname'] . '@' . $baseUrl->getHost() . ($baseUrl->getPath() ? '/' . $baseUrl->getPath() : ''));
|
||||
$uri = urlencode('acct:' . $profile['nickname'] . '@' . DI::baseUrl()->getHost() . (DI::baseUrl()->getPath() ? '/' . DI::baseUrl()->getPath() : ''));
|
||||
|
||||
$page['htmlhead'] .= '<meta name="dfrn-global-visibility" content="' . ($profile['net-publish'] ? 'true' : 'false') . '" />' . "\r\n";
|
||||
$page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . $baseUrl . '/dfrn_poll/' . $nickname . '" />' . "\r\n";
|
||||
|
|
|
@ -31,23 +31,25 @@ use Friendica\Util\Strings;
|
|||
*/
|
||||
class Hashtag extends BaseModule
|
||||
{
|
||||
protected function content(array $request = []): string
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$t = Strings::escapeHtml($_REQUEST['t']);
|
||||
if (empty($t)) {
|
||||
if (empty($request['t'])) {
|
||||
System::jsonExit($result);
|
||||
}
|
||||
|
||||
$taglist = DBA::select('tag', ['name'], ["`name` LIKE ?", $t . "%"], ['order' => ['name'], 'limit' => 100]);
|
||||
$taglist = DBA::select(
|
||||
'tag',
|
||||
['name'],
|
||||
["`name` LIKE ?", Strings::escapeHtml($request['t']) . "%"],
|
||||
['order' => ['name'], 'limit' => 100]
|
||||
);
|
||||
while ($tag = DBA::fetch($taglist)) {
|
||||
$result[] = ['text' => $tag['name']];
|
||||
}
|
||||
DBA::close($taglist);
|
||||
|
||||
System::jsonExit($result);
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,6 +191,7 @@ class Compose extends BaseModule
|
|||
'editalic' => $this->l10n->t('Italic'),
|
||||
'eduline' => $this->l10n->t('Underline'),
|
||||
'edquote' => $this->l10n->t('Quote'),
|
||||
'$edemojis' => $this->l10n->t('Add emojis'),
|
||||
'edcode' => $this->l10n->t('Code'),
|
||||
'edimg' => $this->l10n->t('Image'),
|
||||
'edurl' => $this->l10n->t('Link'),
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Module;
|
||||
|
||||
use Exception;
|
||||
use Friendica\App;
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\L10n;
|
||||
|
@ -30,7 +31,6 @@ use Friendica\Database\Database;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
use Friendica\Util\Profiler;
|
||||
|
@ -65,82 +65,102 @@ class Magic extends BaseModule
|
|||
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$this->logger->info('magic module: invoked');
|
||||
|
||||
$this->logger->debug('args', ['request' => $_REQUEST]);
|
||||
|
||||
$addr = $_REQUEST['addr'] ?? '';
|
||||
$dest = $_REQUEST['dest'] ?? '';
|
||||
$owa = (!empty($_REQUEST['owa']) ? intval($_REQUEST['owa']) : 0);
|
||||
$cid = 0;
|
||||
|
||||
if (!empty($addr)) {
|
||||
$cid = Contact::getIdForURL($addr);
|
||||
} elseif (!empty($dest)) {
|
||||
$cid = Contact::getIdForURL($dest);
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
|
||||
$this->logger->debug('Got a HEAD request');
|
||||
System::exit();
|
||||
}
|
||||
|
||||
if (!$cid) {
|
||||
$this->logger->info('No contact record found', $_REQUEST);
|
||||
// @TODO Finding a more elegant possibility to redirect to either internal or external URL
|
||||
$this->logger->debug('Invoked', ['request' => $request]);
|
||||
|
||||
$addr = $request['addr'] ?? '';
|
||||
$dest = $request['dest'] ?? '';
|
||||
$bdest = $request['bdest'] ?? '';
|
||||
$owa = intval($request['owa'] ?? 0);
|
||||
|
||||
// bdest is preferred as it is hex-encoded and can survive url rewrite and argument parsing
|
||||
if (!empty($bdest)) {
|
||||
$dest = hex2bin($bdest);
|
||||
$this->logger->debug('bdest detected', ['dest' => $dest]);
|
||||
}
|
||||
|
||||
if ($addr ?: $dest) {
|
||||
$contact = Contact::getByURL($addr ?: $dest);
|
||||
}
|
||||
|
||||
if (empty($contact)) {
|
||||
if (!$owa) {
|
||||
$this->logger->info('No contact record found, no oWA, redirecting to destination.', ['request' => $request, 'server' => $_SERVER, 'dest' => $dest]);
|
||||
$this->app->redirect($dest);
|
||||
}
|
||||
} else {
|
||||
// Redirect if the contact is already authenticated on this site.
|
||||
if ($this->app->getContactId() && strpos($contact['nurl'], Strings::normaliseLink($this->baseUrl)) !== false) {
|
||||
$this->logger->info('Contact is already authenticated, redirecting to destination.', ['dest' => $dest]);
|
||||
System::externalRedirect($dest);
|
||||
}
|
||||
|
||||
$this->logger->debug('Contact found', ['url' => $contact['url']]);
|
||||
}
|
||||
|
||||
if (!$this->userSession->getLocalUserId() || !$owa) {
|
||||
$this->logger->notice('Not logged in or not OWA, redirecting to destination.', ['uid' => $this->userSession->getLocalUserId(), 'owa' => $owa, 'dest' => $dest]);
|
||||
$this->app->redirect($dest);
|
||||
}
|
||||
$contact = $this->dba->selectFirst('contact', ['id', 'nurl', 'url'], ['id' => $cid]);
|
||||
|
||||
// Redirect if the contact is already authenticated on this site.
|
||||
if ($this->app->getContactId() && strpos($contact['nurl'], Strings::normaliseLink($this->baseUrl)) !== false) {
|
||||
$this->logger->info('Contact is already authenticated');
|
||||
System::externalRedirect($dest);
|
||||
}
|
||||
|
||||
// OpenWebAuth
|
||||
if ($this->userSession->getLocalUserId() && $owa) {
|
||||
$user = User::getById($this->userSession->getLocalUserId());
|
||||
$owner = User::getOwnerDataById($this->userSession->getLocalUserId());
|
||||
|
||||
// Extract the basepath
|
||||
// NOTE: we need another solution because this does only work
|
||||
// for friendica contacts :-/ . We should have the basepath
|
||||
// of a contact also in the contact table.
|
||||
$exp = explode('/profile/', $contact['url']);
|
||||
$basepath = $exp[0];
|
||||
|
||||
$header = [
|
||||
'Accept' => ['application/x-dfrn+json', 'application/x-zot+json'],
|
||||
'X-Open-Web-Auth' => [Strings::getRandomHex()],
|
||||
];
|
||||
|
||||
// Create a header that is signed with the local users private key.
|
||||
$header = HTTPSignature::createSig(
|
||||
$header,
|
||||
$user['prvkey'],
|
||||
'acct:' . $user['nickname'] . '@' . $this->baseUrl->getHost() . ($this->baseUrl->getPath() ? '/' . $this->baseUrl->getPath() : '')
|
||||
);
|
||||
|
||||
// Try to get an authentication token from the other instance.
|
||||
$curlResult = $this->httpClient->get($basepath . '/owa', HttpClientAccept::DEFAULT, [HttpClientOptions::HEADERS => $header]);
|
||||
|
||||
if ($curlResult->isSuccess()) {
|
||||
$j = json_decode($curlResult->getBody(), true);
|
||||
|
||||
if ($j['success']) {
|
||||
$token = '';
|
||||
if ($j['encrypted_token']) {
|
||||
// The token is encrypted. If the local user is really the one the other instance
|
||||
// thinks he/she is, the token can be decrypted with the local users public key.
|
||||
openssl_private_decrypt(Strings::base64UrlDecode($j['encrypted_token']), $token, $user['prvkey']);
|
||||
} else {
|
||||
$token = $j['token'];
|
||||
}
|
||||
$args = (strpbrk($dest, '?&') ? '&' : '?') . 'owt=' . $token;
|
||||
|
||||
$this->logger->info('Redirecting', ['path' => $dest . $args]);
|
||||
System::externalRedirect($dest . $args);
|
||||
}
|
||||
}
|
||||
$gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]);
|
||||
if (empty($gserver)) {
|
||||
$this->logger->notice('Server not found, redirecting to destination.', ['gsid' => $contact['gsid'], 'dest' => $dest]);
|
||||
System::externalRedirect($dest);
|
||||
}
|
||||
|
||||
// @TODO Finding a more elegant possibility to redirect to either internal or external URL
|
||||
$this->app->redirect($dest);
|
||||
$basepath = $gserver['url'];
|
||||
|
||||
$header = [
|
||||
'Accept' => ['application/x-dfrn+json', 'application/x-zot+json'],
|
||||
'X-Open-Web-Auth' => [Strings::getRandomHex()],
|
||||
];
|
||||
|
||||
// Create a header that is signed with the local users private key.
|
||||
$header = HTTPSignature::createSig(
|
||||
$header,
|
||||
$owner['prvkey'],
|
||||
'acct:' . $owner['addr']
|
||||
);
|
||||
|
||||
$this->logger->info('Fetch from remote system', ['basepath' => $basepath, 'headers' => $header]);
|
||||
|
||||
// Try to get an authentication token from the other instance.
|
||||
try {
|
||||
$curlResult = $this->httpClient->request('get', $basepath . '/owa', [HttpClientOptions::HEADERS => $header]);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->notice('URL is invalid, redirecting to destination.', ['url' => $basepath, 'error' => $exception, 'dest' => $dest]);
|
||||
System::externalRedirect($dest);
|
||||
}
|
||||
if (!$curlResult->isSuccess()) {
|
||||
$this->logger->notice('OWA request failed, redirecting to destination.', ['returncode' => $curlResult->getReturnCode(), 'dest' => $dest]);
|
||||
System::externalRedirect($dest);
|
||||
}
|
||||
|
||||
$j = json_decode($curlResult->getBody(), true);
|
||||
if (empty($j) || !$j['success']) {
|
||||
$this->logger->notice('Invalid JSON, redirecting to destination.', ['json' => $j, 'dest' => $dest]);
|
||||
$this->app->redirect($dest);
|
||||
}
|
||||
|
||||
if ($j['encrypted_token']) {
|
||||
// The token is encrypted. If the local user is really the one the other instance
|
||||
// thinks they is, the token can be decrypted with the local users public key.
|
||||
$token = '';
|
||||
openssl_private_decrypt(Strings::base64UrlDecode($j['encrypted_token']), $token, $owner['prvkey']);
|
||||
} else {
|
||||
$token = $j['token'];
|
||||
}
|
||||
$args = (strpbrk($dest, '?&') ? '&' : '?') . 'owt=' . $token;
|
||||
|
||||
$this->logger->debug('Redirecting', ['path' => $dest . $args]);
|
||||
System::externalRedirect($dest . $args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
|
|||
use Friendica\Object\Image;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
@ -164,34 +163,8 @@ class Upload extends \Friendica\BaseModule
|
|||
$this->logger->info('File upload: Scaling picture to new size', ['max_length' => $max_length]);
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand($this->config->get('system', 'maximagesize'));
|
||||
|
||||
if ($maximagesize && $filesize > $maximagesize) {
|
||||
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
|
||||
foreach ([5120, 2560, 1280, 640] as $pixels) {
|
||||
if ($filesize > $maximagesize && max($width, $height) > $pixels) {
|
||||
$this->logger->info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
if ($filesize > $maximagesize) {
|
||||
@unlink($src);
|
||||
$this->logger->notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
|
||||
$this->return(401, $this->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)));
|
||||
}
|
||||
}
|
||||
|
||||
$resource_id = Photo::newResource();
|
||||
|
||||
$smallest = 0;
|
||||
|
||||
// If we don't have an album name use the Wall Photos album
|
||||
if (!strlen($album)) {
|
||||
$album = $this->t('Wall Photos');
|
||||
|
@ -199,30 +172,14 @@ class Upload extends \Friendica\BaseModule
|
|||
|
||||
$allow_cid = '<' . $owner['id'] . '>';
|
||||
|
||||
$result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 0, Photo::DEFAULT, $allow_cid);
|
||||
if (!$result) {
|
||||
$this->logger->warning('Photo::store() failed', ['result' => $result]);
|
||||
$preview = Photo::storeWithPreview($image, $owner['uid'], $resource_id, $filename, $filesize, $album, '', $allow_cid, '', '', '');
|
||||
if ($preview < 0) {
|
||||
$this->logger->warning('Photo::store() failed');
|
||||
$this->return(401, $this->t('Image upload failed.'));
|
||||
}
|
||||
|
||||
if ($width > 640 || $height > 640) {
|
||||
$image->scaleDown(640);
|
||||
$result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 1, Photo::DEFAULT, $allow_cid);
|
||||
if ($result) {
|
||||
$smallest = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($width > 320 || $height > 320) {
|
||||
$image->scaleDown(320);
|
||||
$result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 2, Photo::DEFAULT, $allow_cid);
|
||||
if ($result && ($smallest == 0)) {
|
||||
$smallest = 2;
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info('upload done');
|
||||
$this->return(200, "\n\n" . '[url=' . $this->baseUrl . '/photos/' . $owner['nickname'] . '/image/' . $resource_id . '][img]' . $this->baseUrl . "/photo/$resource_id-$smallest." . $image->getExt() . "[/img][/url]\n\n");
|
||||
$this->return(200, "\n\n" . Images::getBBCodeByResource($resource_id, $owner['nickname'], $preview, $image->getExt()) . "\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,7 +40,7 @@ class OpenSearch extends BaseModule
|
|||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$hostname = DI::baseUrl()->getHost();
|
||||
$baseUrl = DI::baseUrl();
|
||||
$baseUrl = (string)DI::baseUrl();
|
||||
|
||||
/** @var DOMDocument $xml */
|
||||
XML::fromArray([
|
||||
|
|
|
@ -190,7 +190,7 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
}
|
||||
|
||||
$receivers = [];
|
||||
foreach (Tag::getByURIId($uriId, [Tag::TO, Tag::CC, Tag::BCC]) as $receiver) {
|
||||
foreach (Tag::getByURIId($uriId, [Tag::TO, Tag::CC, Tag::BCC, Tag::AUDIENCE, Tag::ATTRIBUTED]) as $receiver) {
|
||||
// We only display BCC when it contains the current user
|
||||
if (($receiver['type'] == Tag::BCC) && ($receiver['url'] != $own_url)) {
|
||||
continue;
|
||||
|
@ -236,6 +236,12 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
case Tag::BCC:
|
||||
$output .= DI::l10n()->t('<b>BCC:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
case Tag::AUDIENCE:
|
||||
$output .= DI::l10n()->t('<b>Audience:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
case Tag::ATTRIBUTED:
|
||||
$output .= DI::l10n()->t('<b>Attributed To:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ class Edit extends BaseModule
|
|||
'$editalic' => $this->t('Italic'),
|
||||
'$eduline' => $this->t('Underline'),
|
||||
'$edquote' => $this->t('Quote'),
|
||||
'$edemojis' => $this->t('Add emojis'),
|
||||
'$edcode' => $this->t('Code'),
|
||||
'$edurl' => $this->t('Link'),
|
||||
'$edattach' => $this->t('Link or Media'),
|
||||
|
|
|
@ -229,33 +229,15 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
$image->scaleDown($max_length);
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
$smallest = 0;
|
||||
|
||||
$resource_id = Photo::newResource();
|
||||
|
||||
$r = Photo::store($image, $this->owner['uid'], 0, $resource_id, $filename, $album, 0 , Photo::DEFAULT, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||
|
||||
if (!$r) {
|
||||
$preview = Photo::storeWithPreview($image, $this->owner['uid'], $resource_id, $filename, $filesize, $album, '', $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||
if ($preview < 0) {
|
||||
$this->logger->warning('image store failed');
|
||||
$this->systemMessages->addNotice($this->t('Image upload failed.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($width > 640 || $height > 640) {
|
||||
$image->scaleDown(640);
|
||||
Photo::store($image, $this->owner['uid'], 0, $resource_id, $filename, $album, 1, Photo::DEFAULT, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||
$smallest = 1;
|
||||
}
|
||||
|
||||
if ($width > 320 || $height > 320) {
|
||||
$image->scaleDown(320);
|
||||
Photo::store($image, $this->owner['uid'], 0, $resource_id, $filename, $album, 2, Photo::DEFAULT, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||
$smallest = 2;
|
||||
}
|
||||
|
||||
$uri = Item::newURI();
|
||||
|
||||
// Create item container
|
||||
|
@ -291,9 +273,7 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
$arr['visible'] = $visible;
|
||||
$arr['origin'] = 1;
|
||||
|
||||
$arr['body'] = '[url=' . $this->baseUrl . '/photos/' . $this->owner['nickname'] . '/image/' . $resource_id . ']'
|
||||
. '[img]' . $this->baseUrl . "/photo/{$resource_id}-{$smallest}.".$image->getExt() . '[/img]'
|
||||
. '[/url]';
|
||||
$arr['body'] = Images::getBBCodeByResource($resource_id, $this->owner['nickname'], $preview, $image->getExt());
|
||||
|
||||
$item_id = Item::insert($arr);
|
||||
// Update the photo albums cache
|
||||
|
@ -354,7 +334,7 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
$sql_extra
|
||||
GROUP BY `resource-id`
|
||||
ORDER BY `created` DESC
|
||||
LIMIT ? , ?",
|
||||
LIMIT ? , ?",
|
||||
$this->owner['uid'],
|
||||
Photo::DEFAULT,
|
||||
$pager->getStart(),
|
||||
|
|
|
@ -298,7 +298,7 @@ class Register extends BaseModule
|
|||
|
||||
$user = $result['user'];
|
||||
|
||||
$base_url = DI::baseUrl();
|
||||
$base_url = (string)DI::baseUrl();
|
||||
|
||||
if ($netpublish && intval(DI::config()->get('config', 'register_policy')) !== self::APPROVE) {
|
||||
$url = $base_url . '/profile/' . $user['nickname'];
|
||||
|
|
|
@ -46,7 +46,7 @@ class HostMeta extends BaseModule
|
|||
$config->set('system', 'site_pubkey', $res['pubkey']);
|
||||
}
|
||||
|
||||
$domain = DI::baseUrl();
|
||||
$domain = (string)DI::baseUrl();
|
||||
|
||||
XML::fromArray([
|
||||
'XRD' => [
|
||||
|
|
|
@ -65,13 +65,19 @@ class Xrd extends BaseModule
|
|||
|
||||
if (substr($uri, 0, 4) === 'http') {
|
||||
$name = ltrim(basename($uri), '~');
|
||||
$host = parse_url($uri, PHP_URL_HOST);
|
||||
} else {
|
||||
$local = str_replace('acct:', '', $uri);
|
||||
if (substr($local, 0, 2) == '//') {
|
||||
$local = substr($local, 2);
|
||||
}
|
||||
|
||||
$name = substr($local, 0, strpos($local, '@'));
|
||||
list($name, $host) = explode('@', $local);
|
||||
}
|
||||
|
||||
if (!empty($host) && $host !== DI::baseUrl()->getHost()) {
|
||||
DI::logger()->notice('Invalid host name for xrd query',['host' => $host, 'uri' => $uri]);
|
||||
throw new NotFoundException('Invalid host name for xrd query: ' . $host);
|
||||
}
|
||||
|
||||
if ($name == User::getActorName()) {
|
||||
|
|
|
@ -61,15 +61,15 @@ class Notify extends BaseEntity
|
|||
protected $photo;
|
||||
/** @var DateTime */
|
||||
protected $date;
|
||||
/** @var string */
|
||||
/** @var string|null */
|
||||
protected $msg;
|
||||
/** @var int */
|
||||
protected $uid;
|
||||
/** @var UriInterface */
|
||||
protected $link;
|
||||
/** @var int */
|
||||
/** @var int|null */
|
||||
protected $itemId;
|
||||
/** @var int */
|
||||
/** @var int|null */
|
||||
protected $parent;
|
||||
/** @var bool */
|
||||
protected $seen;
|
||||
|
@ -79,13 +79,13 @@ class Notify extends BaseEntity
|
|||
protected $otype;
|
||||
/** @var string */
|
||||
protected $name_cache;
|
||||
/** @var string */
|
||||
/** @var string|null */
|
||||
protected $msg_cache;
|
||||
/** @var int|null */
|
||||
protected $uriId;
|
||||
/** @var int|null */
|
||||
protected $parentUriId;
|
||||
/** @var int */
|
||||
/** @var int|null */
|
||||
protected $id;
|
||||
|
||||
public function __construct(int $type, string $name, UriInterface $url, UriInterface $photo, DateTime $date, int $uid, UriInterface $link, bool $seen, string $verb, string $otype, string $name_cache, string $msg = null, string $msg_cache = null, int $itemId = null, int $uriId = null, int $parent = null, ?int $parentUriId = null, ?int $id = null)
|
||||
|
@ -134,6 +134,6 @@ class Notify extends BaseEntity
|
|||
*/
|
||||
public static function formatMessage(string $name, string $message): string
|
||||
{
|
||||
return str_replace('{0}', '<span class="contactname">' . strip_tags(BBCode::convert($name)) . '</span>', $message);
|
||||
return str_replace('{0}', '<span class="contactname">' . strip_tags(BBCode::convert($name)) . '</span>', htmlspecialchars($message));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ class FormattedNotify extends BaseFactory
|
|||
$this->baseUrl . '/notify/' . $Notify->id,
|
||||
Contact::getAvatarUrlForUrl($Notify->url, $Notify->uid, Proxy::SIZE_MICRO),
|
||||
$Notify->url,
|
||||
strip_tags(BBCode::toPlaintext($Notify->msg)),
|
||||
strip_tags(BBCode::toPlaintext($Notify->msg ?? '')),
|
||||
DateTimeFormat::local($Notify->date->format(DateTimeFormat::MYSQL), 'r'),
|
||||
Temporal::getRelativeDate($Notify->date->format(DateTimeFormat::MYSQL)),
|
||||
$Notify->seen
|
||||
|
|
|
@ -952,9 +952,17 @@ class Probe
|
|||
*/
|
||||
public static function webfinger(string $url, string $type): array
|
||||
{
|
||||
$xrd_timeout = DI::config()->get('system', 'xrd_timeout', 20);
|
||||
try {
|
||||
$curlResult = DI::httpClient()->get(
|
||||
$url,
|
||||
$type,
|
||||
[HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout', 20)]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
Logger::notice($e->getMessage(), ['url' => $url, 'type' => $type, 'class' => get_class($e)]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$curlResult = DI::httpClient()->get($url, $type, [HttpClientOptions::TIMEOUT => $xrd_timeout]);
|
||||
if ($curlResult->isTimeout()) {
|
||||
self::$isTimeout = true;
|
||||
return [];
|
||||
|
|
|
@ -247,9 +247,10 @@ class Post
|
|||
// Showing the one or the other text, depending upon if we can only hide it or really delete it.
|
||||
$delete = $origin ? DI::l10n()->t('Delete globally') : DI::l10n()->t('Remove locally');
|
||||
|
||||
$drop = false;
|
||||
$block = false;
|
||||
$ignore = false;
|
||||
$drop = false;
|
||||
$block = false;
|
||||
$ignore = false;
|
||||
$collapse = false;
|
||||
if (DI::userSession()->getLocalUserId()) {
|
||||
$drop = [
|
||||
'dropping' => $dropping,
|
||||
|
@ -270,6 +271,11 @@ class Post
|
|||
'ignore' => DI::l10n()->t('Ignore %s', $item['author-name']),
|
||||
'author_id' => $item['author-id'],
|
||||
];
|
||||
$collapse = [
|
||||
'collapsing' => true,
|
||||
'collapse' => DI::l10n()->t('Collapse %s', $item['author-name']),
|
||||
'author_id' => $item['author-id'],
|
||||
];
|
||||
}
|
||||
|
||||
$filer = DI::userSession()->getLocalUserId() ? DI::l10n()->t('Save to folder') : false;
|
||||
|
@ -536,6 +542,7 @@ class Post
|
|||
'drop' => $drop,
|
||||
'block' => $block,
|
||||
'ignore_author' => $ignore,
|
||||
'collapse' => $collapse,
|
||||
'vote' => $buttons,
|
||||
'like_html' => $responses['like']['output'],
|
||||
'dislike_html' => $responses['dislike']['output'],
|
||||
|
@ -1066,6 +1073,7 @@ class Post
|
|||
'$editalic' => DI::l10n()->t('Italic'),
|
||||
'$eduline' => DI::l10n()->t('Underline'),
|
||||
'$edquote' => DI::l10n()->t('Quote'),
|
||||
'$edemojis' => DI::l10n()->t('Add emojis'),
|
||||
'$edcode' => DI::l10n()->t('Code'),
|
||||
'$edimg' => DI::l10n()->t('Image'),
|
||||
'$edurl' => DI::l10n()->t('Link'),
|
||||
|
|
|
@ -145,6 +145,12 @@ final class ActivityNamespace
|
|||
*/
|
||||
const ATOM1 = 'http://www.w3.org/2005/Atom';
|
||||
|
||||
/**
|
||||
* This namespace is used for the (deprecated) Atom 0.3 specification
|
||||
* @var string
|
||||
*/
|
||||
const ATOM03 = 'http://purl.org/atom/ns#';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -208,7 +208,7 @@ class ClientToServer
|
|||
|
||||
$targets = [];
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc', 'as:audience'] as $element) {
|
||||
switch ($element) {
|
||||
case 'as:to':
|
||||
$type = Receiver::TARGET_TO;
|
||||
|
@ -222,6 +222,9 @@ class ClientToServer
|
|||
case 'as:bcc':
|
||||
$type = Receiver::TARGET_BCC;
|
||||
break;
|
||||
case 'as:audience':
|
||||
$type = Receiver::TARGET_AUDIENCE;
|
||||
break;
|
||||
}
|
||||
$receiver_list = JsonLD::fetchElementArray($object, $element, '@id');
|
||||
if (empty($receiver_list)) {
|
||||
|
|
|
@ -1026,6 +1026,9 @@ class Processor
|
|||
case Receiver::TARGET_BCC:
|
||||
$item['post-reason'] = Item::PR_BCC;
|
||||
break;
|
||||
case Receiver::TARGET_AUDIENCE:
|
||||
$item['post-reason'] = Item::PR_AUDIENCE;
|
||||
break;
|
||||
case Receiver::TARGET_FOLLOWER:
|
||||
$item['post-reason'] = Item::PR_FOLLOWER;
|
||||
break;
|
||||
|
@ -1071,7 +1074,7 @@ class Processor
|
|||
continue;
|
||||
}
|
||||
|
||||
if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC])) {
|
||||
if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC, Item::PR_AUDIENCE])) {
|
||||
if (!($item['isForum'] ?? false)) {
|
||||
if ($item['post-reason'] == Item::PR_BCC) {
|
||||
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id'], 'url' => $item['uri']]);
|
||||
|
@ -1274,7 +1277,7 @@ class Processor
|
|||
|
||||
public static function storeReceivers(int $uriid, array $receivers)
|
||||
{
|
||||
foreach (['as:to' => Tag::TO, 'as:cc' => Tag::CC, 'as:bto' => Tag::BTO, 'as:bcc' => Tag::BCC] as $element => $type) {
|
||||
foreach (['as:to' => Tag::TO, 'as:cc' => Tag::CC, 'as:bto' => Tag::BTO, 'as:bcc' => Tag::BCC, 'as:audience' => Tag::AUDIENCE, 'as:attributedTo' => Tag::ATTRIBUTED] as $element => $type) {
|
||||
if (!empty($receivers[$element])) {
|
||||
foreach ($receivers[$element] as $receiver) {
|
||||
if ($receiver == ActivityPub::PUBLIC_COLLECTION) {
|
||||
|
|
|
@ -312,7 +312,7 @@ class Queue
|
|||
// Optimizing this table only last seconds
|
||||
if (DI::config()->get('system', 'optimize_tables')) {
|
||||
Logger::info('Optimize start');
|
||||
DBA::e("OPTIMIZE TABLE `inbox-entry`");
|
||||
DBA::optimizeTable('inbox-entry');
|
||||
Logger::info('Optimize end');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ class Receiver
|
|||
const TARGET_FOLLOWER = 5;
|
||||
const TARGET_ANSWER = 6;
|
||||
const TARGET_GLOBAL = 7;
|
||||
const TARGET_AUDIENCE = 8;
|
||||
|
||||
const COMPLETION_NONE = 0;
|
||||
const COMPLETION_ANNOUNCE = 1;
|
||||
|
@ -395,15 +396,22 @@ class Receiver
|
|||
|
||||
// Fetch the activity on Lemmy "Announce" messages (announces of activities)
|
||||
if (($type == 'as:Announce') && in_array($object_type, array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) {
|
||||
Logger::debug('Fetch announced activity', ['object' => $object_id]);
|
||||
Logger::debug('Fetch announced activity', ['object' => $object_id, 'uid' => $fetch_uid]);
|
||||
$data = Processor::fetchCachedActivity($object_id, $fetch_uid);
|
||||
if (!empty($data)) {
|
||||
$type = $object_type;
|
||||
$activity = JsonLD::compact($data);
|
||||
$announced_activity = JsonLD::compact($data);
|
||||
|
||||
// Some variables need to be refetched since the activity changed
|
||||
$actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
|
||||
$object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$actor = JsonLD::fetchElement($announced_activity, 'as:actor', '@id');
|
||||
$announced_id = JsonLD::fetchElement($announced_activity, 'as:object', '@id');
|
||||
if (empty($announced_id)) {
|
||||
Logger::warning('No object id in announced activity', ['id' => $object_id, 'activity' => $activity, 'announced' => $announced_activity]);
|
||||
return [];
|
||||
} else {
|
||||
$activity = $announced_activity;
|
||||
$object_id = $announced_id;
|
||||
}
|
||||
$object_type = self::fetchObjectType($activity, $object_id, $fetch_uid);
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +495,7 @@ class Receiver
|
|||
$object_data['object_type'] = $object_type;
|
||||
}
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc', 'as:audience', 'as:attributedTo'] as $element) {
|
||||
if ((empty($object_data['receiver_urls'][$element]) || in_array($element, ['as:bto', 'as:bcc'])) && !empty($urls[$element])) {
|
||||
$object_data['receiver_urls'][$element] = array_unique(array_merge($object_data['receiver_urls'][$element] ?? [], $urls[$element]));
|
||||
}
|
||||
|
@ -1032,7 +1040,7 @@ class Receiver
|
|||
{
|
||||
$urls = [];
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc', 'as:audience', 'as:attributedTo'] as $element) {
|
||||
$receiver_list = JsonLD::fetchElementArray($activity, $element, '@id');
|
||||
if (empty($receiver_list)) {
|
||||
continue;
|
||||
|
@ -1104,7 +1112,7 @@ class Receiver
|
|||
// We have to prevent false follower assumptions upon thread completions
|
||||
$follower_target = empty($activity['thread-completion']) ? self::TARGET_FOLLOWER : self::TARGET_UNKNOWN;
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc','as:audience'] as $element) {
|
||||
$receiver_list = JsonLD::fetchElementArray($activity, $element, '@id');
|
||||
if (empty($receiver_list)) {
|
||||
continue;
|
||||
|
@ -1165,6 +1173,9 @@ class Receiver
|
|||
case 'as:bcc':
|
||||
$type = self::TARGET_BCC;
|
||||
break;
|
||||
case 'as:audience':
|
||||
$type = self::TARGET_AUDIENCE;
|
||||
break;
|
||||
}
|
||||
|
||||
$receivers[$contact['uid']] = ['uid' => $contact['uid'], 'type' => $type];
|
||||
|
|
|
@ -330,7 +330,7 @@ class Transmitter
|
|||
return [
|
||||
'type' => 'Service',
|
||||
'name' => App::PLATFORM . " '" . App::CODENAME . "' " . App::VERSION . '-' . DB_UPDATE_VERSION,
|
||||
'url' => DI::baseUrl()
|
||||
'url' => (string)DI::baseUrl()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -557,15 +557,16 @@ class Transmitter
|
|||
/**
|
||||
* Creates an array of permissions from an item thread
|
||||
*
|
||||
* @param array $item Item array
|
||||
* @param boolean $blindcopy addressing via "bcc" or "cc"?
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
* @param array $item Item array
|
||||
* @param boolean $blindcopy addressing via "bcc" or "cc"?
|
||||
* @param boolean $expand_followers Expand the list of followers
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
*
|
||||
* @return array with permission data
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function createPermissionBlockForItem(array $item, bool $blindcopy, int $last_id = 0): array
|
||||
private static function createPermissionBlockForItem(array $item, bool $blindcopy, bool $expand_followers, int $last_id = 0): array
|
||||
{
|
||||
if ($last_id == 0) {
|
||||
$last_id = $item['id'];
|
||||
|
@ -607,7 +608,7 @@ class Transmitter
|
|||
$networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS];
|
||||
}
|
||||
|
||||
$data = ['to' => [], 'cc' => [], 'bcc' => []];
|
||||
$data = ['to' => [], 'cc' => [], 'bcc' => [] , 'audience' => []];
|
||||
|
||||
if ($item['gravity'] == Item::GRAVITY_PARENT) {
|
||||
$actor_profile = APContact::getByURL($item['owner-link']);
|
||||
|
@ -658,7 +659,8 @@ class Transmitter
|
|||
if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
|
||||
$exclusive = true;
|
||||
if (!empty($profile['followers']) && ($profile['type'] == 'Group')) {
|
||||
$data['cc'][] = $profile['followers'];
|
||||
$data['cc'][] = $profile['followers'];
|
||||
$data['audience'][] = $profile['url'];
|
||||
}
|
||||
} elseif (($term['type'] == Tag::MENTION) && ($profile['type'] == 'Group')) {
|
||||
$mention = true;
|
||||
|
@ -666,8 +668,11 @@ class Transmitter
|
|||
$data['to'][] = $profile['url'];
|
||||
}
|
||||
}
|
||||
if (!$exclusive && ($item['private'] == Item::UNLISTED)) {
|
||||
$data['to'][] = $actor_profile['followers'];
|
||||
}
|
||||
} else {
|
||||
$receiver_list = Item::enumeratePermissions($item, true);
|
||||
$receiver_list = Item::enumeratePermissions($item, true, $expand_followers);
|
||||
|
||||
foreach ($terms as $term) {
|
||||
$cid = Contact::getIdForURL($term['url'], $item['uid']);
|
||||
|
@ -682,7 +687,8 @@ class Transmitter
|
|||
if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
|
||||
$exclusive = true;
|
||||
if (!empty($profile['followers']) && ($profile['type'] == 'Group')) {
|
||||
$data['cc'][] = $profile['followers'];
|
||||
$data['cc'][] = $profile['followers'];
|
||||
$data['audience'][] = $profile['url'];
|
||||
}
|
||||
} elseif (($term['type'] == Tag::MENTION) && ($profile['type'] == 'Group')) {
|
||||
$mention = true;
|
||||
|
@ -700,6 +706,11 @@ class Transmitter
|
|||
$data['cc'][] = $follower;
|
||||
} elseif (!$exclusive) {
|
||||
foreach ($receiver_list as $receiver) {
|
||||
if ($receiver == -1) {
|
||||
$data['to'][] = $actor_profile['followers'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]);
|
||||
if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) {
|
||||
continue;
|
||||
|
@ -766,9 +777,10 @@ class Transmitter
|
|||
DBA::close($parents);
|
||||
}
|
||||
|
||||
$data['to'] = array_unique($data['to']);
|
||||
$data['cc'] = array_unique($data['cc']);
|
||||
$data['bcc'] = array_unique($data['bcc']);
|
||||
$data['to'] = array_unique($data['to']);
|
||||
$data['cc'] = array_unique($data['cc']);
|
||||
$data['bcc'] = array_unique($data['bcc']);
|
||||
$data['audience'] = array_unique($data['audience']);
|
||||
|
||||
if (($key = array_search($item['author-link'], $data['to'])) !== false) {
|
||||
unset($data['to'][$key]);
|
||||
|
@ -782,6 +794,10 @@ class Transmitter
|
|||
unset($data['bcc'][$key]);
|
||||
}
|
||||
|
||||
if (($key = array_search($item['author-link'], $data['audience'])) !== false) {
|
||||
unset($data['audience'][$key]);
|
||||
}
|
||||
|
||||
foreach ($data['to'] as $to) {
|
||||
if (($key = array_search($to, $data['cc'])) !== false) {
|
||||
unset($data['cc'][$key]);
|
||||
|
@ -800,6 +816,13 @@ class Transmitter
|
|||
|
||||
$receivers = ['to' => array_values($data['to']), 'cc' => array_values($data['cc']), 'bcc' => array_values($data['bcc'])];
|
||||
|
||||
if (!empty($data['audience'])) {
|
||||
$receivers['audience'] = array_values($data['audience']);
|
||||
if (count($receivers['audience']) == 1) {
|
||||
$receivers['audience'] = $receivers['audience'][0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!$blindcopy) {
|
||||
unset($receivers['bcc']);
|
||||
}
|
||||
|
@ -935,7 +958,7 @@ class Transmitter
|
|||
*/
|
||||
public static function fetchTargetInboxes(array $item, int $uid, bool $personal = false, int $last_id = 0): array
|
||||
{
|
||||
$permissions = self::createPermissionBlockForItem($item, true, $last_id);
|
||||
$permissions = self::createPermissionBlockForItem($item, true, true, $last_id);
|
||||
if (empty($permissions)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -1067,7 +1090,7 @@ class Transmitter
|
|||
$data['actor'] = $mail['author-link'];
|
||||
$data['published'] = DateTimeFormat::utc($mail['created'] . '+00:00', DateTimeFormat::ATOM);
|
||||
$data['instrument'] = self::getService();
|
||||
$data = array_merge($data, self::createPermissionBlockForItem($mail, true));
|
||||
$data = array_merge($data, self::createPermissionBlockForItem($mail, true, false));
|
||||
|
||||
if (empty($data['to']) && !empty($data['cc'])) {
|
||||
$data['to'] = $data['cc'];
|
||||
|
@ -1292,7 +1315,7 @@ class Transmitter
|
|||
|
||||
$data['instrument'] = self::getService();
|
||||
|
||||
$data = array_merge($data, self::createPermissionBlockForItem($item, false));
|
||||
$data = array_merge($data, self::createPermissionBlockForItem($item, false, false));
|
||||
|
||||
if (in_array($data['type'], ['Create', 'Update', 'Delete'])) {
|
||||
$data['object'] = self::createNote($item, $api_mode);
|
||||
|
@ -1640,7 +1663,7 @@ class Transmitter
|
|||
$data['name'] = BBCode::toPlaintext($item['title'], false);
|
||||
}
|
||||
|
||||
$permission_block = self::createPermissionBlockForItem($item, false);
|
||||
$permission_block = self::createPermissionBlockForItem($item, false, false);
|
||||
|
||||
$real_quote = false;
|
||||
|
||||
|
|
|
@ -92,7 +92,9 @@ class Feed
|
|||
$doc = new DOMDocument();
|
||||
@$doc->loadXML($xml);
|
||||
$xpath = new DOMXPath($doc);
|
||||
|
||||
$xpath->registerNamespace('atom', ActivityNamespace::ATOM1);
|
||||
$xpath->registerNamespace('atom03', ActivityNamespace::ATOM03);
|
||||
$xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
||||
$xpath->registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/');
|
||||
$xpath->registerNamespace('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
|
||||
|
@ -101,6 +103,7 @@ class Feed
|
|||
$xpath->registerNamespace('poco', ActivityNamespace::POCO);
|
||||
|
||||
$author = [];
|
||||
$atomns = 'atom';
|
||||
$entries = null;
|
||||
$protocol = Conversation::PARCEL_UNKNOWN;
|
||||
|
||||
|
@ -116,10 +119,22 @@ class Feed
|
|||
$entries = $xpath->query('/rdf:RDF/rss:item');
|
||||
}
|
||||
|
||||
if ($xpath->query('/opml')->length > 0) {
|
||||
$protocol = Conversation::PARCEL_OPML;
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/opml/head/title/text()');
|
||||
$entries = $xpath->query('/opml/body/outline');
|
||||
}
|
||||
|
||||
// Is it Atom?
|
||||
if ($xpath->query('/atom:feed')->length > 0) {
|
||||
$protocol = Conversation::PARCEL_ATOM;
|
||||
$alternate = XML::getFirstAttributes($xpath, "atom:link[@rel='alternate']");
|
||||
} elseif ($xpath->query('/atom03:feed')->length > 0) {
|
||||
$protocol = Conversation::PARCEL_ATOM03;
|
||||
$atomns = 'atom03';
|
||||
}
|
||||
|
||||
if (in_array($protocol, [Conversation::PARCEL_ATOM, Conversation::PARCEL_ATOM03])) {
|
||||
$alternate = XML::getFirstAttributes($xpath, $atomns . ":link[@rel='alternate']");
|
||||
if (is_object($alternate)) {
|
||||
foreach ($alternate as $attribute) {
|
||||
if ($attribute->name == 'href') {
|
||||
|
@ -129,7 +144,7 @@ class Feed
|
|||
}
|
||||
|
||||
if (empty($author['author-link'])) {
|
||||
$self = XML::getFirstAttributes($xpath, "atom:link[@rel='self']");
|
||||
$self = XML::getFirstAttributes($xpath, $atomns . ":link[@rel='self']");
|
||||
if (is_object($self)) {
|
||||
foreach ($self as $attribute) {
|
||||
if ($attribute->name == 'href') {
|
||||
|
@ -140,50 +155,50 @@ class Feed
|
|||
}
|
||||
|
||||
if (empty($author['author-link'])) {
|
||||
$author['author-link'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:id/text()');
|
||||
$author['author-link'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':id/text()');
|
||||
}
|
||||
$author['author-avatar'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:logo/text()');
|
||||
$author['author-avatar'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':logo/text()');
|
||||
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:title/text()');
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':title/text()');
|
||||
|
||||
if (empty($author['author-name'])) {
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:subtitle/text()');
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':subtitle/text()');
|
||||
}
|
||||
|
||||
if (empty($author['author-name'])) {
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:author/atom:name/text()');
|
||||
$author['author-name'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':author/' . $atomns . ':name/text()');
|
||||
}
|
||||
|
||||
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:displayName/text()');
|
||||
$value = XML::getFirstNodeValue($xpath, '' . $atomns . ':author/poco:displayName/text()');
|
||||
if ($value != '') {
|
||||
$author['author-name'] = $value;
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$author['author-id'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:author/atom:id/text()');
|
||||
$author['author-id'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':author/' . $atomns . ':id/text()');
|
||||
|
||||
// See https://tools.ietf.org/html/rfc4287#section-3.2.2
|
||||
$value = XML::getFirstNodeValue($xpath, 'atom:author/atom:uri/text()');
|
||||
$value = XML::getFirstNodeValue($xpath, $atomns . ':author/' . $atomns . ':uri/text()');
|
||||
if ($value != '') {
|
||||
$author['author-link'] = $value;
|
||||
}
|
||||
|
||||
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:preferredUsername/text()');
|
||||
$value = XML::getFirstNodeValue($xpath, $atomns . ':author/poco:preferredUsername/text()');
|
||||
if ($value != '') {
|
||||
$author['author-nick'] = $value;
|
||||
}
|
||||
|
||||
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:address/poco:formatted/text()');
|
||||
$value = XML::getFirstNodeValue($xpath, $atomns . ':author/poco:address/poco:formatted/text()');
|
||||
if ($value != '') {
|
||||
$author['author-location'] = $value;
|
||||
}
|
||||
|
||||
$value = XML::getFirstNodeValue($xpath, 'atom:author/poco:note/text()');
|
||||
$value = XML::getFirstNodeValue($xpath, $atomns . ':author/poco:note/text()');
|
||||
if ($value != '') {
|
||||
$author['author-about'] = $value;
|
||||
}
|
||||
|
||||
$avatar = XML::getFirstAttributes($xpath, "atom:author/atom:link[@rel='avatar']");
|
||||
$avatar = XML::getFirstAttributes($xpath, $atomns . ":author/' . $atomns . ':link[@rel='avatar']");
|
||||
if (is_object($avatar)) {
|
||||
foreach ($avatar as $attribute) {
|
||||
if ($attribute->name == 'href') {
|
||||
|
@ -193,11 +208,11 @@ class Feed
|
|||
}
|
||||
}
|
||||
|
||||
$author['edited'] = $author['created'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:updated/text()');
|
||||
$author['edited'] = $author['created'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':updated/text()');
|
||||
|
||||
$author['app'] = XML::getFirstNodeValue($xpath, '/atom:feed/atom:generator/text()');
|
||||
$author['app'] = XML::getFirstNodeValue($xpath, '/' . $atomns . ':feed/' . $atomns . ':generator/text()');
|
||||
|
||||
$entries = $xpath->query('/atom:feed/atom:entry');
|
||||
$entries = $xpath->query('/' . $atomns . ':feed/' . $atomns . ':entry');
|
||||
}
|
||||
|
||||
// Is it RSS?
|
||||
|
@ -293,10 +308,11 @@ class Feed
|
|||
$entry = $entries->item($i);
|
||||
|
||||
$item = array_merge($header, $author);
|
||||
$body = '';
|
||||
|
||||
$alternate = XML::getFirstAttributes($xpath, "atom:link[@rel='alternate']", $entry);
|
||||
$alternate = XML::getFirstAttributes($xpath, $atomns . ":link[@rel='alternate']", $entry);
|
||||
if (!is_object($alternate)) {
|
||||
$alternate = XML::getFirstAttributes($xpath, 'atom:link', $entry);
|
||||
$alternate = XML::getFirstAttributes($xpath, $atomns . ':link', $entry);
|
||||
}
|
||||
if (is_object($alternate)) {
|
||||
foreach ($alternate as $attribute) {
|
||||
|
@ -306,6 +322,40 @@ class Feed
|
|||
}
|
||||
}
|
||||
|
||||
if ($entry->nodeName == 'outline') {
|
||||
$isrss = false;
|
||||
$plink = '';
|
||||
$uri = '';
|
||||
foreach ($entry->attributes as $attribute) {
|
||||
switch ($attribute->nodeName) {
|
||||
case 'title':
|
||||
$item['title'] = $attribute->nodeValue;
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
$body = $attribute->nodeValue;
|
||||
break;
|
||||
|
||||
case 'htmlUrl':
|
||||
$plink = $attribute->nodeValue;
|
||||
break;
|
||||
|
||||
case 'xmlUrl':
|
||||
$uri = $attribute->nodeValue;
|
||||
break;
|
||||
|
||||
case 'type':
|
||||
$isrss = $attribute->nodeValue == 'rss';
|
||||
break;
|
||||
}
|
||||
}
|
||||
$item['plink'] = $plink ?: $uri;
|
||||
$item['uri'] = $uri ?: $plink;
|
||||
if (!$isrss || empty($item['uri'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($item['plink'])) {
|
||||
$item['plink'] = XML::getFirstNodeValue($xpath, 'link/text()', $entry);
|
||||
}
|
||||
|
@ -317,7 +367,9 @@ class Feed
|
|||
// Add the base path if missing
|
||||
$item['plink'] = Network::addBasePath($item['plink'], $basepath);
|
||||
|
||||
$item['uri'] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
|
||||
if (empty($item['uri'])) {
|
||||
$item['uri'] = XML::getFirstNodeValue($xpath, $atomns . ':id/text()', $entry);
|
||||
}
|
||||
|
||||
$guid = XML::getFirstNodeValue($xpath, 'guid/text()', $entry);
|
||||
if (!empty($guid)) {
|
||||
|
@ -339,7 +391,9 @@ class Feed
|
|||
Logger::notice('Item URL couldn\'t get expanded', ['url' => $item['plink'], 'exception' => $exception]);
|
||||
}
|
||||
|
||||
$item['title'] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry);
|
||||
if (empty($item['title'])) {
|
||||
$item['title'] = XML::getFirstNodeValue($xpath, $atomns . ':title/text()', $entry);
|
||||
}
|
||||
|
||||
if (empty($item['title'])) {
|
||||
$item['title'] = XML::getFirstNodeValue($xpath, 'title/text()', $entry);
|
||||
|
@ -355,7 +409,7 @@ class Feed
|
|||
|
||||
$item['title'] = html_entity_decode($item['title'], ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$published = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
|
||||
$published = XML::getFirstNodeValue($xpath, $atomns . ':published/text()', $entry);
|
||||
|
||||
if (empty($published)) {
|
||||
$published = XML::getFirstNodeValue($xpath, 'pubDate/text()', $entry);
|
||||
|
@ -365,7 +419,7 @@ class Feed
|
|||
$published = XML::getFirstNodeValue($xpath, 'dc:date/text()', $entry);
|
||||
}
|
||||
|
||||
$updated = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
|
||||
$updated = XML::getFirstNodeValue($xpath, $atomns . ':updated/text()', $entry);
|
||||
|
||||
if (empty($updated) && !empty($published)) {
|
||||
$updated = $published;
|
||||
|
@ -401,7 +455,7 @@ class Feed
|
|||
$creator = XML::getFirstNodeValue($xpath, 'author/text()', $entry);
|
||||
|
||||
if (empty($creator)) {
|
||||
$creator = XML::getFirstNodeValue($xpath, 'atom:author/atom:name/text()', $entry);
|
||||
$creator = XML::getFirstNodeValue($xpath, $atomns . ':author/' . $atomns . ':name/text()', $entry);
|
||||
}
|
||||
|
||||
if (empty($creator)) {
|
||||
|
@ -424,33 +478,35 @@ class Feed
|
|||
|
||||
$attachments = [];
|
||||
|
||||
$enclosures = $xpath->query("enclosure|atom:link[@rel='enclosure']", $entry);
|
||||
foreach ($enclosures as $enclosure) {
|
||||
$href = '';
|
||||
$length = null;
|
||||
$type = null;
|
||||
$enclosures = $xpath->query("enclosure|$atomns:link[@rel='enclosure']", $entry);
|
||||
if (!empty($enclosures)) {
|
||||
foreach ($enclosures as $enclosure) {
|
||||
$href = '';
|
||||
$length = null;
|
||||
$type = null;
|
||||
|
||||
foreach ($enclosure->attributes as $attribute) {
|
||||
if (in_array($attribute->name, ['url', 'href'])) {
|
||||
$href = $attribute->textContent;
|
||||
} elseif ($attribute->name == 'length') {
|
||||
$length = (int)$attribute->textContent;
|
||||
} elseif ($attribute->name == 'type') {
|
||||
$type = $attribute->textContent;
|
||||
foreach ($enclosure->attributes as $attribute) {
|
||||
if (in_array($attribute->name, ['url', 'href'])) {
|
||||
$href = $attribute->textContent;
|
||||
} elseif ($attribute->name == 'length') {
|
||||
$length = (int)$attribute->textContent;
|
||||
} elseif ($attribute->name == 'type') {
|
||||
$type = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($href)) {
|
||||
$attachment = ['uri-id' => -1, 'type' => Post\Media::UNKNOWN, 'url' => $href, 'mimetype' => $type, 'size' => $length];
|
||||
if (!empty($href)) {
|
||||
$attachment = ['uri-id' => -1, 'type' => Post\Media::UNKNOWN, 'url' => $href, 'mimetype' => $type, 'size' => $length];
|
||||
|
||||
$attachment = Post\Media::fetchAdditionalData($attachment);
|
||||
$attachment = Post\Media::fetchAdditionalData($attachment);
|
||||
|
||||
// By now we separate the visible media types (audio, video, image) from the rest
|
||||
// In the future we should try to avoid the DOCUMENT type and only use the real one - but not in the RC phase.
|
||||
if (!in_array($attachment['type'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO])) {
|
||||
$attachment['type'] = Post\Media::DOCUMENT;
|
||||
// By now we separate the visible media types (audio, video, image) from the rest
|
||||
// In the future we should try to avoid the DOCUMENT type and only use the real one - but not in the RC phase.
|
||||
if (!in_array($attachment['type'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO])) {
|
||||
$attachment['type'] = Post\Media::DOCUMENT;
|
||||
}
|
||||
$attachments[] = $attachment;
|
||||
}
|
||||
$attachments[] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,13 +516,15 @@ class Feed
|
|||
$taglist[] = $category->nodeValue;
|
||||
}
|
||||
|
||||
$body = trim(XML::getFirstNodeValue($xpath, 'atom:content/text()', $entry));
|
||||
if (empty($body)) {
|
||||
$body = trim(XML::getFirstNodeValue($xpath, $atomns . ':content/text()', $entry));
|
||||
}
|
||||
|
||||
if (empty($body)) {
|
||||
$body = trim(XML::getFirstNodeValue($xpath, 'content:encoded/text()', $entry));
|
||||
}
|
||||
|
||||
$summary = trim(XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry));
|
||||
$summary = trim(XML::getFirstNodeValue($xpath, $atomns . ':summary/text()', $entry));
|
||||
|
||||
if (empty($summary)) {
|
||||
$summary = trim(XML::getFirstNodeValue($xpath, 'description/text()', $entry));
|
||||
|
|
|
@ -177,7 +177,7 @@ class BasicAuth
|
|||
}
|
||||
Logger::debug('Access denied', ['parameters' => $_SERVER]);
|
||||
// Checking for commandline for the tests, we have to avoid to send a header
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
if (DI::config()->get('system', 'basicauth') && (php_sapi_name() !== 'cli')) {
|
||||
header('WWW-Authenticate: Basic realm="Friendica"');
|
||||
}
|
||||
throw new UnauthorizedException("This API requires login");
|
||||
|
|
|
@ -316,4 +316,40 @@ class Images
|
|||
|
||||
return ['width' => $dest_width, 'height' => $dest_height];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a BBCode tag for an local photo page URL with a preview thumbnail and an image description
|
||||
*
|
||||
* @param string $resource_id
|
||||
* @param string $nickname The local user owner of the resource
|
||||
* @param int $preview Preview image size identifier, either 0, 1 or 2 in decreasing order of size
|
||||
* @param string $ext Image file extension
|
||||
* @param string $description
|
||||
* @return string
|
||||
*/
|
||||
public static function getBBCodeByResource(string $resource_id, string $nickname, int $preview, string $ext, string $description = ''): string
|
||||
{
|
||||
return self::getBBCodeByUrl(
|
||||
DI::baseUrl() . '/photos/' . $nickname . '/image/' . $resource_id,
|
||||
DI::baseUrl() . '/photo/' . $resource_id . '-' . $preview. '.' . $ext,
|
||||
$description
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a BBCode tag for an image URL with a preview thumbnail and an image description
|
||||
*
|
||||
* @param string $photo Full image URL
|
||||
* @param string $preview Preview image URL
|
||||
* @param string $description
|
||||
* @return string
|
||||
*/
|
||||
public static function getBBCodeByUrl(string $photo, string $preview = null, string $description = ''): string
|
||||
{
|
||||
if (!empty($preview)) {
|
||||
return '[url=' . $photo . '][img=' . $preview . ']' . $description . '[/img][/url]';
|
||||
}
|
||||
|
||||
return '[img=' . $photo . ']' . $description . '[/img]';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,15 +163,6 @@ class Cron
|
|||
{
|
||||
Logger::info('Looking for sleeping processes');
|
||||
|
||||
$processes = DBA::p("SHOW FULL PROCESSLIST");
|
||||
while ($process = DBA::fetch($processes)) {
|
||||
if (($process['Command'] != 'Sleep') || ($process['Time'] < 300) || ($process['db'] != DBA::databaseName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DBA::e("KILL ?", $process['Id']);
|
||||
Logger::notice('Killed sleeping process', ['id' => $process['Id']]);
|
||||
}
|
||||
DBA::close($processes);
|
||||
DBA::deleteSleepingProcesses();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,36 +40,36 @@ class OptimizeTables
|
|||
|
||||
Logger::info('Optimize start');
|
||||
|
||||
DBA::e("OPTIMIZE TABLE `cache`");
|
||||
DBA::e("OPTIMIZE TABLE `locks`");
|
||||
DBA::e("OPTIMIZE TABLE `oembed`");
|
||||
DBA::e("OPTIMIZE TABLE `parsed_url`");
|
||||
DBA::e("OPTIMIZE TABLE `session`");
|
||||
DBA::optimizeTable('cache');
|
||||
DBA::optimizeTable('locks');
|
||||
DBA::optimizeTable('oembed');
|
||||
DBA::optimizeTable('parsed_url');
|
||||
DBA::optimizeTable('session');
|
||||
|
||||
if (DI::config()->get('system', 'optimize_all_tables')) {
|
||||
DBA::e("OPTIMIZE TABLE `apcontact`");
|
||||
DBA::e("OPTIMIZE TABLE `contact`");
|
||||
DBA::e("OPTIMIZE TABLE `contact-relation`");
|
||||
DBA::e("OPTIMIZE TABLE `conversation`");
|
||||
DBA::e("OPTIMIZE TABLE `diaspora-contact`");
|
||||
DBA::e("OPTIMIZE TABLE `diaspora-interaction`");
|
||||
DBA::e("OPTIMIZE TABLE `fcontact`");
|
||||
DBA::e("OPTIMIZE TABLE `gserver`");
|
||||
DBA::e("OPTIMIZE TABLE `gserver-tag`");
|
||||
DBA::e("OPTIMIZE TABLE `inbox-status`");
|
||||
DBA::e("OPTIMIZE TABLE `item-uri`");
|
||||
DBA::e("OPTIMIZE TABLE `notification`");
|
||||
DBA::e("OPTIMIZE TABLE `notify`");
|
||||
DBA::e("OPTIMIZE TABLE `photo`");
|
||||
DBA::e("OPTIMIZE TABLE `post`");
|
||||
DBA::e("OPTIMIZE TABLE `post-content`");
|
||||
DBA::e("OPTIMIZE TABLE `post-delivery-data`");
|
||||
DBA::e("OPTIMIZE TABLE `post-link`");
|
||||
DBA::e("OPTIMIZE TABLE `post-thread`");
|
||||
DBA::e("OPTIMIZE TABLE `post-thread-user`");
|
||||
DBA::e("OPTIMIZE TABLE `post-user`");
|
||||
DBA::e("OPTIMIZE TABLE `storage`");
|
||||
DBA::e("OPTIMIZE TABLE `tag`");
|
||||
DBA::optimizeTable('apcontact');
|
||||
DBA::optimizeTable('contact');
|
||||
DBA::optimizeTable('contact-relation');
|
||||
DBA::optimizeTable('conversation');
|
||||
DBA::optimizeTable('diaspora-contact');
|
||||
DBA::optimizeTable('diaspora-interaction');
|
||||
DBA::optimizeTable('fcontact');
|
||||
DBA::optimizeTable('gserver');
|
||||
DBA::optimizeTable('gserver-tag');
|
||||
DBA::optimizeTable('inbox-status');
|
||||
DBA::optimizeTable('item-uri');
|
||||
DBA::optimizeTable('notification');
|
||||
DBA::optimizeTable('notify');
|
||||
DBA::optimizeTable('photo');
|
||||
DBA::optimizeTable('post');
|
||||
DBA::optimizeTable('post-content');
|
||||
DBA::optimizeTable('post-delivery-data');
|
||||
DBA::optimizeTable('post-link');
|
||||
DBA::optimizeTable('post-thread');
|
||||
DBA::optimizeTable('post-thread-user');
|
||||
DBA::optimizeTable('post-user');
|
||||
DBA::optimizeTable('storage');
|
||||
DBA::optimizeTable('tag');
|
||||
}
|
||||
|
||||
Logger::info('Optimize end');
|
||||
|
|
|
@ -132,6 +132,10 @@ return [
|
|||
// The value has to start with the scheme and end with a "/"
|
||||
'avatar_cache_url' => '',
|
||||
|
||||
// basicauth (Boolean)
|
||||
// Controls if login via BasicAuth is possible (default is true)
|
||||
'basicauth' => true,
|
||||
|
||||
// big_emojis (Boolean)
|
||||
// Display "Emoji Only" posts in big.
|
||||
'big_emojis' => false,
|
||||
|
@ -337,6 +341,14 @@ return [
|
|||
// Resolve IPV4 addresses only. Don't resolve to IPV6.
|
||||
'ipv4_resolve' => false,
|
||||
|
||||
// ini_max_execution_time (False|Integer)
|
||||
// Set the number of seconds a script is allowed to run. Default unlimited for Friendica, false to use the system value.
|
||||
'ini_max_execution_time' => 0,
|
||||
|
||||
// ini_pcre_backtrack_limit (False|Integer)
|
||||
// This has to be quite large to deal with embedded private photos. False to use the system value.
|
||||
'ini_pcre_backtrack_limit' => 500000,
|
||||
|
||||
// invitation_only (Boolean)
|
||||
// If set true registration is only possible after a current member of the node has sent an invitation.
|
||||
'invitation_only' => false,
|
||||
|
|
|
@ -35,6 +35,15 @@ return [
|
|||
'workerqueue',
|
||||
'mail',
|
||||
'post-delivery-data',
|
||||
'gserver' => [
|
||||
[
|
||||
'url' => 'https://friendica.local',
|
||||
'nurl' => 'http://friendica.local',
|
||||
'register_policy' => 0,
|
||||
'registered-users' => 0,
|
||||
'network' => 'unkn',
|
||||
],
|
||||
],
|
||||
// Base test config to avoid notice messages
|
||||
'user' => [
|
||||
[
|
||||
|
|
|
@ -259,12 +259,12 @@ Karl Marx - Die ursprüngliche Akkumulation
|
|||
'text' => '[emoji=https://fedi.underscore.world/emoji/custom/custom/heart_nb.png]:heart_nb:[/emoji]',
|
||||
],
|
||||
'task-12900-multiple-paragraphs' => [
|
||||
'expectedHTML' => '<h1>Header</h1><ul><li>One</li><li>Two</li></ul><p>This is a paragraph<br>with a line feed.</p><p>Second Chapter</p>',
|
||||
'text' => "[h1]Header[/h1][ul][*]One[*]Two[/ul]\n\nThis is a paragraph\nwith a line feed.\n\nSecond Chapter",
|
||||
'expectedHTML' => '<h4>Header</h4><ul><li>One</li><li>Two</li></ul><p>This is a paragraph<br>with a line feed.</p><p>Second Chapter</p>',
|
||||
'text' => "[h4]Header[/h4][ul][*]One[*]Two[/ul]\n\nThis is a paragraph\nwith a line feed.\n\nSecond Chapter",
|
||||
],
|
||||
'task-12900-header-with-paragraphs' => [
|
||||
'expectedHTML' => '<h1>Header</h1><p>Some Chapter</p>',
|
||||
'text' => '[h1]Header[/h1]Some Chapter',
|
||||
'expectedHTML' => '<h4>Header</h4><p>Some Chapter</p>',
|
||||
'text' => '[h4]Header[/h4]Some Chapter',
|
||||
],
|
||||
'bug-12842-ul-newlines' => [
|
||||
'expectedHTML' => '<p>This is:</p><ul><li>some</li><li>amazing</li><li>list</li></ul>',
|
||||
|
|
103
tests/src/Database/DatabaseTest.php
Normal file
103
tests/src/Database/DatabaseTest.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Test\src\Database;
|
||||
|
||||
use Friendica\Core\Config\Util\ConfigFileManager;
|
||||
use Friendica\Core\Config\ValueObject\Cache;
|
||||
use Friendica\Test\FixtureTest;
|
||||
use Friendica\Test\Util\CreateDatabaseTrait;
|
||||
|
||||
class DatabaseTest extends FixtureTest
|
||||
{
|
||||
use CreateDatabaseTrait;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->setUpVfsDir();
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->configCache = new Cache();
|
||||
$this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test, if directly updating a field is possible
|
||||
*/
|
||||
public function testUpdateIncrease()
|
||||
{
|
||||
$db = $this->getDbInstance();
|
||||
|
||||
self::assertTrue($db->insert('config', ['cat' => 'test', 'k' => 'inc', 'v' => 0]));
|
||||
self::assertTrue($db->update('config', ["`v` = `v` + 1"], ['cat' => 'test', 'k' => 'inc']));
|
||||
self::assertEquals(1, $db->selectFirst('config', ['v'], ['cat' => 'test', 'k' => 'inc'])['v']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if combining directly field updates with normal updates is working
|
||||
*/
|
||||
public function testUpdateWithField()
|
||||
{
|
||||
$db = $this->getDbInstance();
|
||||
|
||||
self::assertEquals('https://friendica.local', $db->selectFirst('gserver', ['url'], ['nurl' => 'http://friendica.local'])['url']);
|
||||
self::assertTrue($db->update('gserver', ['active-week-users' => 0], ['nurl' => 'http://friendica.local']));
|
||||
self::assertTrue($db->update('gserver', [
|
||||
'site_name' => 'test', "`registered-users` = `registered-users` + 1",
|
||||
'info' => 'another test',
|
||||
"`active-week-users` = `active-week-users` + 2"
|
||||
], [
|
||||
'nurl' => 'http://friendica.local'
|
||||
]));
|
||||
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
|
||||
self::assertEquals(2, $db->selectFirst('gserver', ['active-week-users'], ['nurl' => 'http://friendica.local'])['active-week-users']);
|
||||
self::assertTrue($db->update('gserver', [
|
||||
'site_name' => 'test', "`registered-users` = `registered-users` + 1",
|
||||
'info' => 'another test'
|
||||
], [
|
||||
'nurl' => 'http://friendica.local'
|
||||
]));
|
||||
self::assertEquals(2, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
|
||||
self::assertTrue($db->update('gserver', [
|
||||
'site_name' => 'test', "`registered-users` = `registered-users` - 1",
|
||||
'info' => 'another test'
|
||||
], [
|
||||
'nurl' => 'http://friendica.local'
|
||||
]));
|
||||
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
|
||||
}
|
||||
|
||||
public function testUpdateWithArray()
|
||||
{
|
||||
$db = $this->getDbInstance();
|
||||
|
||||
self::assertTrue($db->update('gserver', ['active-week-users' => 0, 'registered-users' => 0], ['nurl' => 'http://friendica.local']));
|
||||
|
||||
$fields = ["`registered-users` = `registered-users` + 1"];
|
||||
$fields[] = "`active-week-users` = `active-week-users` + 2";
|
||||
|
||||
self::assertTrue($db->update('gserver', $fields, ['nurl' => 'http://friendica.local']));
|
||||
|
||||
self::assertEquals(2, $db->selectFirst('gserver', ['active-week-users'], ['nurl' => 'http://friendica.local'])['active-week-users']);
|
||||
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
|
||||
}
|
||||
}
|
47
tests/src/Navigation/Notifications/Entity/NotifyTest.php
Normal file
47
tests/src/Navigation/Notifications/Entity/NotifyTest.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Test\src\Navigation\Notifications\Entity;
|
||||
|
||||
use Friendica\Navigation\Notifications\Entity\Notify;
|
||||
use Friendica\Test\FixtureTest;
|
||||
|
||||
class NotifyTest extends FixtureTest
|
||||
{
|
||||
public function dataFormatNotify(): array
|
||||
{
|
||||
return [
|
||||
'xss-notify' => [
|
||||
'name' => 'Whiskers',
|
||||
'message' => '{0} commented in the thread "If my username causes a pop up in a piece of software, that softwar…" from <script>alert("Tek");</script>',
|
||||
'assertion' => '<span class="contactname">Whiskers</span> commented in the thread "If my username causes a pop up in a piece of software, that softwar…" from <script>alert("Tek");</script>',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataFormatNotify
|
||||
*/
|
||||
public function testFormatNotify(string $name, string $message, string $assertion)
|
||||
{
|
||||
self::assertEquals($assertion, Notify::formatMessage($name, $message));
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue