Compare commits
281 Commits
develop
...
2022.05-rc
Author | SHA1 | Date | |
---|---|---|---|
0b312ae9b3 | |||
d7eb78e65e | |||
6ec812208f | |||
406b3f14dd | |||
ee40d8ea04 | |||
7f711e266f | |||
9160fde8ca | |||
220703575c | |||
1e68c90fb2 | |||
aabb56c70d | |||
47a3d8e6ce | |||
4c5d9e22a8 | |||
121d26999c | |||
540b3f892b | |||
1301a53f20 | |||
f1630ebb05 | |||
7d09ce86c4 | |||
42d3e5c1ce | |||
697b8a6cb8 | |||
1dccc31508 | |||
92bf5213cc | |||
02473d8a0e | |||
511a51b9a3 | |||
a924716131 | |||
faa3f26d3b | |||
a503d5ac1b | |||
875d84435c | |||
4ef7f42257 | |||
881c4204b4 | |||
ba8088bf3c | |||
c7608983dc | |||
efa15ab0b8 | |||
d3624af1b6 | |||
b05aa824f7 | |||
0a43fe857c | |||
e793438e0d | |||
cf91734f4f | |||
d4631176b2 | |||
4475b3ff39 | |||
8c901ebd74 | |||
1d29025091 | |||
cb5b6f345e | |||
314431507d | |||
620352d1a2 | |||
488fdd9791 | |||
ae3acba231 | |||
36ccb25bd2 | |||
918c1eeb56 | |||
f5d69b3b6a | |||
e8864653d8 | |||
dadebdd061 | |||
737d9e7a58 | |||
2a0ab3f0f1 | |||
9804c71b53 | |||
0de50a5118 | |||
e5e509ea21 | |||
2fbfc0633a | |||
d3709d8497 | |||
9da0e637a3 | |||
80adad3ad2 | |||
fb0e31791f | |||
077c6ece64 | |||
2ead69c7a5 | |||
0374f0e523 | |||
0ff8ca8053 | |||
eddfab7122 | |||
f764ab7429 | |||
74b102b948 | |||
bee6ffe968 | |||
034d838248 | |||
8969e83134 | |||
e68ff3b3bb | |||
b1a2de5cb5 | |||
f16cb13dc7 | |||
9b860f4051 | |||
7b91c4f333 | |||
68599c7efa | |||
299c4df8f4 | |||
d658c81107 | |||
d6242aacf6 | |||
a1871756ee | |||
218fc0c20d | |||
783b05cbb1 | |||
c4ec80e839 | |||
087ad25c87 | |||
54d7a435a3 | |||
ed350e472f | |||
49dab14215 | |||
9e19043c15 | |||
3444c29b0b | |||
11538376ed | |||
c374630c65 | |||
e4e8b8cb46 | |||
5095ce621a | |||
7857c329cf | |||
9ded39eff6 | |||
8af0af5f35 | |||
d858aa86e5 | |||
2fc5957abb | |||
653af77e5f | |||
6ce23bd9ca | |||
dc16e6d471 | |||
87e14d9d28 | |||
a13d33e057 | |||
e151e5958f | |||
49c47008d1 | |||
0b8e0a09e3 | |||
c33611c484 | |||
5aa798b1dc | |||
536ce232a7 | |||
7d958e8804 | |||
b18880221a | |||
23e5ae15e3 | |||
4622814e5f | |||
2e6e3597e5 | |||
f6167b4cfd | |||
4e9d7df31a | |||
aacaa3c2cf | |||
4016a576d5 | |||
6eb70bea16 | |||
0b50dc363b | |||
c3fd8b39aa | |||
4e3e9dc763 | |||
75534fa3f3 | |||
73019284ce | |||
a880d8b982 | |||
610f8a086f | |||
3493f91801 | |||
10cdefa232 | |||
e555ea6aad | |||
1ccf22a496 | |||
9f1cc2fb67 | |||
3f8da997f4 | |||
65516a1d3a | |||
257ae2ac17 | |||
4daae255d8 | |||
f07422634a | |||
62a1cf257e | |||
69984ac6bc | |||
7199a91289 | |||
2dc9b4eda2 | |||
f277d9cd2f | |||
1555c82196 | |||
a63caefe3d | |||
4829c240e9 | |||
2bde5c4850 | |||
2c511b8a23 | |||
440b6d7956 | |||
d77b043d9c | |||
324cce7a19 | |||
204e52ea30 | |||
972f91436c | |||
6f70d21e07 | |||
5af7c30026 | |||
66004f4d2d | |||
44291a465b | |||
30b9af10ac | |||
036b565a78 | |||
f0bfa9a690 | |||
689b71c696 | |||
fbdb73cdd6 | |||
43dbd1c396 | |||
f8d929d94e | |||
962b35a76a | |||
a8839517fe | |||
30bcb24af7 | |||
4b5a743645 | |||
74167043a8 | |||
4ef2679ca6 | |||
fab5ba39ff | |||
065d73f860 | |||
65b86fe0d5 | |||
0f0f4bc2c7 | |||
bb44ff1528 | |||
a662245c74 | |||
47cd1edb9f | |||
201610dfe6 | |||
2595b5e12f | |||
2049fbce91 | |||
4f68be82ef | |||
67a74c15e1 | |||
5409998047 | |||
b0b67f1fde | |||
dfeb2e9a62 | |||
ce7bf8524d | |||
a943dbb420 | |||
d5d2892f59 | |||
f128c00ca5 | |||
3d7ecb4fde | |||
610b3fc39e | |||
afa57edf8e | |||
fc364df7c2 | |||
47ee6fd009 | |||
449df1a583 | |||
d816f02ab8 | |||
7026dd37db | |||
0f0b649e8d | |||
34f594137e | |||
ffb8491c3f | |||
020ba7a4ed | |||
695e3d8b61 | |||
b5129eb4ed | |||
47808ab0e9 | |||
e374c2e3da | |||
d9de8b6d2f | |||
fae414fedd | |||
776ce3f63a | |||
4be52cdc4e | |||
7c3173a0ae | |||
945f5c6db2 | |||
d195d934be | |||
22da88b43f | |||
548bf469ca | |||
c50ca418a5 | |||
630d25a24b | |||
34030a736d | |||
916aa1c9a9 | |||
e1f32f7f15 | |||
cbe4a42906 | |||
f785026289 | |||
f744dd362d | |||
9d1ff0a4ce | |||
006b7a95f0 | |||
681d19a3bc | |||
53d064c283 | |||
e3692c0105 | |||
b9e091eb2f | |||
6b9f543c4e | |||
f220e26f00 | |||
805dc8e6bd | |||
dd422a1e03 | |||
7b0187c4c5 | |||
ee81c266e1 | |||
2d856f48b7 | |||
a31256412e | |||
510dacf4df | |||
25876099ce | |||
166f38ef3b | |||
67c3a20c4f | |||
4729fca5d9 | |||
1e7a55180a | |||
2433fee461 | |||
ab42fd88a4 | |||
2a4b5b4cb4 | |||
4d359b7de1 | |||
d34432c517 | |||
2f74a7bca4 | |||
10b453bfc2 | |||
60532ee7e4 | |||
f6218427a4 | |||
ff8e839139 | |||
4171ff7563 | |||
9953abb8aa | |||
a7237ac036 | |||
fdf8002df1 | |||
4ba28e0199 | |||
49ec5e5e6e | |||
e6440471ae | |||
690682a37e | |||
632a98965c | |||
8bf806c550 | |||
3163760343 | |||
6e394ac6ff | |||
76789acace | |||
fcb245947e | |||
9c2fe81ac6 | |||
c608d85707 | |||
a9990db98c | |||
40aa67f8be | |||
13e4144ba6 | |||
f7b6507438 | |||
19c1b31ab2 | |||
e19681684b | |||
1f43332a1d | |||
fb3353d4bd | |||
fdf70c1047 | |||
a2452b33eb | |||
8dc0ab9bb3 | |||
d3de2497bc | |||
d44641e58c | |||
1326239576 |
|
@ -86,3 +86,7 @@ venv/
|
||||||
|
|
||||||
#Ignore cache file
|
#Ignore cache file
|
||||||
.php_cs.cache
|
.php_cs.cache
|
||||||
|
|
||||||
|
#ignore avatar picture cache path
|
||||||
|
/avatar
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,11 @@ Andy
|
||||||
Andy Hee
|
Andy Hee
|
||||||
Angristan
|
Angristan
|
||||||
Anthronaut
|
Anthronaut
|
||||||
|
Anton
|
||||||
Antron Samurai
|
Antron Samurai
|
||||||
Arian - Cazare Muncitori
|
Arian - Cazare Muncitori
|
||||||
Asher Pen
|
Asher Pen
|
||||||
|
atjn
|
||||||
aweiher
|
aweiher
|
||||||
axelt
|
axelt
|
||||||
balderino
|
balderino
|
||||||
|
@ -55,6 +57,7 @@ Carlos Solís
|
||||||
Carsten Pfeiffer
|
Carsten Pfeiffer
|
||||||
Casper
|
Casper
|
||||||
Cat Gray
|
Cat Gray
|
||||||
|
chinnux
|
||||||
Chris Case
|
Chris Case
|
||||||
Christian González
|
Christian González
|
||||||
Christian Kalkhoff
|
Christian Kalkhoff
|
||||||
|
@ -90,6 +93,7 @@ effex7
|
||||||
Elena
|
Elena
|
||||||
emilia.krawczyk
|
emilia.krawczyk
|
||||||
Eric Côté
|
Eric Côté
|
||||||
|
Erich
|
||||||
erik
|
erik
|
||||||
Erkan Yilmaz
|
Erkan Yilmaz
|
||||||
Eugene Veresk
|
Eugene Veresk
|
||||||
|
@ -113,6 +117,7 @@ Gidi Kroon
|
||||||
GLComo
|
GLComo
|
||||||
greeneyedred
|
greeneyedred
|
||||||
Gregory Smith
|
Gregory Smith
|
||||||
|
gudzpoz
|
||||||
guzzisti
|
guzzisti
|
||||||
Haakon Meland Eriksen
|
Haakon Meland Eriksen
|
||||||
Hans Meine
|
Hans Meine
|
||||||
|
@ -138,6 +143,7 @@ Joe Doe
|
||||||
joe slam
|
joe slam
|
||||||
Johannes Schwab
|
Johannes Schwab
|
||||||
John Brazil
|
John Brazil
|
||||||
|
John Mortensen
|
||||||
Jonatan Nyberg
|
Jonatan Nyberg
|
||||||
Jonny Tischbein
|
Jonny Tischbein
|
||||||
Josef Moravek
|
Josef Moravek
|
||||||
|
@ -216,6 +222,7 @@ Philipp Holzer
|
||||||
Pierre Bernardeau
|
Pierre Bernardeau
|
||||||
Pierre Rudloff
|
Pierre Rudloff
|
||||||
Piotr Blonkowski
|
Piotr Blonkowski
|
||||||
|
Piotr Strębski
|
||||||
pokerazor
|
pokerazor
|
||||||
R C
|
R C
|
||||||
Rabuzarus
|
Rabuzarus
|
||||||
|
@ -284,6 +291,7 @@ Tobias Diekershoff
|
||||||
Tobias Hößl
|
Tobias Hößl
|
||||||
Tom
|
Tom
|
||||||
Tom Aurlund
|
Tom Aurlund
|
||||||
|
Tom Hu
|
||||||
tomamplius
|
tomamplius
|
||||||
tomtom84
|
tomtom84
|
||||||
Tony Baldwin
|
Tony Baldwin
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* 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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
* Run the worker from a daemon.
|
* Run the worker from a daemon.
|
||||||
*
|
*
|
||||||
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php
|
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (php_sapi_name() !== 'cli') {
|
if (php_sapi_name() !== 'cli') {
|
||||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||||
exit();
|
exit();
|
||||||
|
@ -230,7 +232,7 @@ while (true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$timeout = ($seconds >= $wait_interval);
|
$timeout = ($seconds >= $wait_interval);
|
||||||
} while (!$timeout && !Worker::IPCJobsExists());
|
} while (!$timeout && !Worker\IPC::JobsExists());
|
||||||
|
|
||||||
if ($timeout) {
|
if ($timeout) {
|
||||||
$do_cron = true;
|
$do_cron = true;
|
||||||
|
|
|
@ -58,7 +58,7 @@ case "$MODE" in
|
||||||
OUTFILE="$FULLPATH/../view/lang/C/messages.po"
|
OUTFILE="$FULLPATH/../view/lang/C/messages.po"
|
||||||
FINDSTARTDIR="."
|
FINDSTARTDIR="."
|
||||||
# skip addon folder
|
# skip addon folder
|
||||||
FINDOPTS="( -path ./addon -or -path ./addons -or -path ./addons-extra -or -path ./tests -or -path ./view/lang -or -path ./view/smarty3 -or -path ./vendor ) -prune -or"
|
FINDOPTS="( -path ./addon -or -path ./addons -or -path ./addons-extra -or -path ./tests -or -path ./view/lang -or -path ./view/smarty3 -or -path ./vendor -or -path ./local -or -path ./avatar -or -path ./proxy ) -prune -or"
|
||||||
|
|
||||||
F9KVERSION=$(cat ./VERSION);
|
F9KVERSION=$(cat ./VERSION);
|
||||||
echo "Friendica version $F9KVERSION"
|
echo "Friendica version $F9KVERSION"
|
||||||
|
|
2
boot.php
2
boot.php
|
@ -31,7 +31,7 @@ use Friendica\Model\Contact;
|
||||||
|
|
||||||
define('FRIENDICA_PLATFORM', 'Friendica');
|
define('FRIENDICA_PLATFORM', 'Friendica');
|
||||||
define('FRIENDICA_CODENAME', 'Siberian Iris');
|
define('FRIENDICA_CODENAME', 'Siberian Iris');
|
||||||
define('FRIENDICA_VERSION', '2022.05-dev');
|
define('FRIENDICA_VERSION', '2022.05-rc');
|
||||||
define('DFRN_PROTOCOL_VERSION', '2.23');
|
define('DFRN_PROTOCOL_VERSION', '2.23');
|
||||||
define('NEW_TABLE_STRUCTURE_VERSION', 1288);
|
define('NEW_TABLE_STRUCTURE_VERSION', 1288);
|
||||||
|
|
||||||
|
|
92
database.sql
92
database.sql
|
@ -1,6 +1,6 @@
|
||||||
-- ------------------------------------------
|
-- ------------------------------------------
|
||||||
-- Friendica 2022.05-dev (Siberian Iris)
|
-- Friendica 2022.05-rc (Siberian Iris)
|
||||||
-- DB_UPDATE_VERSION 1460
|
-- DB_UPDATE_VERSION 1464
|
||||||
-- ------------------------------------------
|
-- ------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -712,13 +712,16 @@ CREATE TABLE IF NOT EXISTS `hook` (
|
||||||
--
|
--
|
||||||
CREATE TABLE IF NOT EXISTS `inbox-status` (
|
CREATE TABLE IF NOT EXISTS `inbox-status` (
|
||||||
`url` varbinary(255) NOT NULL COMMENT 'URL of the inbox',
|
`url` varbinary(255) NOT NULL COMMENT 'URL of the inbox',
|
||||||
|
`uri-id` int unsigned COMMENT 'Item-uri id of inbox url',
|
||||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date of this entry',
|
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date of this entry',
|
||||||
`success` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful delivery',
|
`success` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful delivery',
|
||||||
`failure` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed delivery',
|
`failure` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed delivery',
|
||||||
`previous` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Previous delivery date',
|
`previous` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Previous delivery date',
|
||||||
`archive` boolean NOT NULL DEFAULT '0' COMMENT 'Is the inbox archived?',
|
`archive` boolean NOT NULL DEFAULT '0' COMMENT 'Is the inbox archived?',
|
||||||
`shared` boolean NOT NULL DEFAULT '0' COMMENT 'Is it a shared inbox?',
|
`shared` boolean NOT NULL DEFAULT '0' COMMENT 'Is it a shared inbox?',
|
||||||
PRIMARY KEY(`url`)
|
PRIMARY KEY(`url`),
|
||||||
|
INDEX `uri-id` (`uri-id`),
|
||||||
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Status of ActivityPub inboxes';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Status of ActivityPub inboxes';
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -1068,8 +1071,8 @@ CREATE TABLE IF NOT EXISTS `post-category` (
|
||||||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||||
PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`),
|
PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`),
|
||||||
INDEX `uri-id` (`tid`),
|
INDEX `tid` (`tid`),
|
||||||
INDEX `uid` (`uid`),
|
INDEX `uid_uri-id` (`uid`,`uri-id`),
|
||||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||||
FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||||
|
@ -1114,6 +1117,25 @@ CREATE TABLE IF NOT EXISTS `post-content` (
|
||||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- TABLE post-delivery
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS `post-delivery` (
|
||||||
|
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||||
|
`inbox-id` int unsigned NOT NULL COMMENT 'Item-uri id of inbox url',
|
||||||
|
`uid` mediumint unsigned COMMENT 'Delivering user',
|
||||||
|
`created` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||||
|
`command` varbinary(32) COMMENT '',
|
||||||
|
`failed` tinyint DEFAULT 0 COMMENT 'Number of times the delivery has failed',
|
||||||
|
`receivers` mediumtext COMMENT 'JSON encoded array with the receiving contacts',
|
||||||
|
PRIMARY KEY(`uri-id`,`inbox-id`),
|
||||||
|
INDEX `inbox-id_created` (`inbox-id`,`created`),
|
||||||
|
INDEX `uid` (`uid`),
|
||||||
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (`inbox-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for posts for the batch processing';
|
||||||
|
|
||||||
--
|
--
|
||||||
-- TABLE post-delivery-data
|
-- TABLE post-delivery-data
|
||||||
--
|
--
|
||||||
|
@ -1133,6 +1155,32 @@ CREATE TABLE IF NOT EXISTS `post-delivery-data` (
|
||||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- TABLE post-history
|
||||||
|
--
|
||||||
|
CREATE TABLE IF NOT EXISTS `post-history` (
|
||||||
|
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
|
||||||
|
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of edit',
|
||||||
|
`title` varchar(255) NOT NULL DEFAULT '' COMMENT 'item title',
|
||||||
|
`content-warning` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||||
|
`body` mediumtext COMMENT 'item body content',
|
||||||
|
`raw-body` mediumtext COMMENT 'Body without embedded media links',
|
||||||
|
`location` varchar(255) NOT NULL DEFAULT '' COMMENT 'text location where this item originated',
|
||||||
|
`coord` varchar(255) NOT NULL DEFAULT '' COMMENT 'longitude/latitude pair representing location where this item originated',
|
||||||
|
`language` text COMMENT 'Language information about this post',
|
||||||
|
`app` varchar(255) NOT NULL DEFAULT '' COMMENT 'application which generated this item',
|
||||||
|
`rendered-hash` varchar(32) NOT NULL DEFAULT '' COMMENT '',
|
||||||
|
`rendered-html` mediumtext COMMENT 'item.body converted to html',
|
||||||
|
`object-type` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams object type',
|
||||||
|
`object` text COMMENT 'JSON encoded object structure unless it is an implied object (normal post)',
|
||||||
|
`target-type` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams target type if applicable (URI)',
|
||||||
|
`target` text COMMENT 'JSON encoded target structure if used',
|
||||||
|
`resource-id` varchar(32) NOT NULL DEFAULT '' COMMENT 'Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type',
|
||||||
|
`plink` varchar(255) NOT NULL DEFAULT '' COMMENT 'permalink or URL to a displayable copy of the message at its source',
|
||||||
|
PRIMARY KEY(`uri-id`,`edited`),
|
||||||
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Post history';
|
||||||
|
|
||||||
--
|
--
|
||||||
-- TABLE post-link
|
-- TABLE post-link
|
||||||
--
|
--
|
||||||
|
@ -1171,6 +1219,7 @@ CREATE TABLE IF NOT EXISTS `post-media` (
|
||||||
`publisher-image` varbinary(255) COMMENT 'Image of the publisher of the media',
|
`publisher-image` varbinary(255) COMMENT 'Image of the publisher of the media',
|
||||||
PRIMARY KEY(`id`),
|
PRIMARY KEY(`id`),
|
||||||
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`),
|
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`),
|
||||||
|
INDEX `uri-id-id` (`uri-id`,`id`),
|
||||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';
|
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';
|
||||||
|
|
||||||
|
@ -1719,6 +1768,9 @@ CREATE VIEW `post-user-view` AS SELECT
|
||||||
`author`.`network` AS `author-network`,
|
`author`.`network` AS `author-network`,
|
||||||
`author`.`blocked` AS `author-blocked`,
|
`author`.`blocked` AS `author-blocked`,
|
||||||
`author`.`hidden` AS `author-hidden`,
|
`author`.`hidden` AS `author-hidden`,
|
||||||
|
`author`.`updated` AS `author-updated`,
|
||||||
|
`author`.`gsid` AS `author-gsid`,
|
||||||
|
`author`.`uri-id` AS `author-uri-id`,
|
||||||
`post-user`.`owner-id` AS `owner-id`,
|
`post-user`.`owner-id` AS `owner-id`,
|
||||||
`owner`.`url` AS `owner-link`,
|
`owner`.`url` AS `owner-link`,
|
||||||
`owner`.`addr` AS `owner-addr`,
|
`owner`.`addr` AS `owner-addr`,
|
||||||
|
@ -1728,6 +1780,7 @@ CREATE VIEW `post-user-view` AS SELECT
|
||||||
`owner`.`network` AS `owner-network`,
|
`owner`.`network` AS `owner-network`,
|
||||||
`owner`.`blocked` AS `owner-blocked`,
|
`owner`.`blocked` AS `owner-blocked`,
|
||||||
`owner`.`hidden` AS `owner-hidden`,
|
`owner`.`hidden` AS `owner-hidden`,
|
||||||
|
`owner`.`updated` AS `owner-updated`,
|
||||||
`owner`.`contact-type` AS `owner-contact-type`,
|
`owner`.`contact-type` AS `owner-contact-type`,
|
||||||
`post-user`.`causer-id` AS `causer-id`,
|
`post-user`.`causer-id` AS `causer-id`,
|
||||||
`causer`.`url` AS `causer-link`,
|
`causer`.`url` AS `causer-link`,
|
||||||
|
@ -1763,6 +1816,8 @@ CREATE VIEW `post-user-view` AS SELECT
|
||||||
`post-question`.`multiple` AS `question-multiple`,
|
`post-question`.`multiple` AS `question-multiple`,
|
||||||
`post-question`.`voters` AS `question-voters`,
|
`post-question`.`voters` AS `question-voters`,
|
||||||
`post-question`.`end-time` AS `question-end-time`,
|
`post-question`.`end-time` AS `question-end-time`,
|
||||||
|
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-user`.`uri-id` AND `post-category`.`uid` = `post-user`.`uid`) AS `has-categories`,
|
||||||
|
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-user`.`uri-id`) AS `has-media`,
|
||||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||||
`parent-post`.`network` AS `parent-network`,
|
`parent-post`.`network` AS `parent-network`,
|
||||||
|
@ -1884,6 +1939,9 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
||||||
`author`.`network` AS `author-network`,
|
`author`.`network` AS `author-network`,
|
||||||
`author`.`blocked` AS `author-blocked`,
|
`author`.`blocked` AS `author-blocked`,
|
||||||
`author`.`hidden` AS `author-hidden`,
|
`author`.`hidden` AS `author-hidden`,
|
||||||
|
`author`.`updated` AS `author-updated`,
|
||||||
|
`author`.`gsid` AS `author-gsid`,
|
||||||
|
`author`.`uri-id` AS `author-uri-id`,
|
||||||
`post-thread-user`.`owner-id` AS `owner-id`,
|
`post-thread-user`.`owner-id` AS `owner-id`,
|
||||||
`owner`.`url` AS `owner-link`,
|
`owner`.`url` AS `owner-link`,
|
||||||
`owner`.`addr` AS `owner-addr`,
|
`owner`.`addr` AS `owner-addr`,
|
||||||
|
@ -1893,6 +1951,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
||||||
`owner`.`network` AS `owner-network`,
|
`owner`.`network` AS `owner-network`,
|
||||||
`owner`.`blocked` AS `owner-blocked`,
|
`owner`.`blocked` AS `owner-blocked`,
|
||||||
`owner`.`hidden` AS `owner-hidden`,
|
`owner`.`hidden` AS `owner-hidden`,
|
||||||
|
`owner`.`updated` AS `owner-updated`,
|
||||||
`owner`.`contact-type` AS `owner-contact-type`,
|
`owner`.`contact-type` AS `owner-contact-type`,
|
||||||
`post-thread-user`.`causer-id` AS `causer-id`,
|
`post-thread-user`.`causer-id` AS `causer-id`,
|
||||||
`causer`.`url` AS `causer-link`,
|
`causer`.`url` AS `causer-link`,
|
||||||
|
@ -1928,6 +1987,8 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
||||||
`post-question`.`multiple` AS `question-multiple`,
|
`post-question`.`multiple` AS `question-multiple`,
|
||||||
`post-question`.`voters` AS `question-voters`,
|
`post-question`.`voters` AS `question-voters`,
|
||||||
`post-question`.`end-time` AS `question-end-time`,
|
`post-question`.`end-time` AS `question-end-time`,
|
||||||
|
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-thread-user`.`uri-id` AND `post-category`.`uid` = `post-thread-user`.`uid`) AS `has-categories`,
|
||||||
|
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread-user`.`uri-id`) AS `has-media`,
|
||||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||||
`parent-post`.`network` AS `parent-network`,
|
`parent-post`.`network` AS `parent-network`,
|
||||||
|
@ -2035,6 +2096,9 @@ CREATE VIEW `post-view` AS SELECT
|
||||||
`author`.`network` AS `author-network`,
|
`author`.`network` AS `author-network`,
|
||||||
`author`.`blocked` AS `author-blocked`,
|
`author`.`blocked` AS `author-blocked`,
|
||||||
`author`.`hidden` AS `author-hidden`,
|
`author`.`hidden` AS `author-hidden`,
|
||||||
|
`author`.`updated` AS `author-updated`,
|
||||||
|
`author`.`gsid` AS `author-gsid`,
|
||||||
|
`author`.`uri-id` AS `author-uri-id`,
|
||||||
`post`.`owner-id` AS `owner-id`,
|
`post`.`owner-id` AS `owner-id`,
|
||||||
`owner`.`url` AS `owner-link`,
|
`owner`.`url` AS `owner-link`,
|
||||||
`owner`.`addr` AS `owner-addr`,
|
`owner`.`addr` AS `owner-addr`,
|
||||||
|
@ -2044,6 +2108,7 @@ CREATE VIEW `post-view` AS SELECT
|
||||||
`owner`.`network` AS `owner-network`,
|
`owner`.`network` AS `owner-network`,
|
||||||
`owner`.`blocked` AS `owner-blocked`,
|
`owner`.`blocked` AS `owner-blocked`,
|
||||||
`owner`.`hidden` AS `owner-hidden`,
|
`owner`.`hidden` AS `owner-hidden`,
|
||||||
|
`owner`.`updated` AS `owner-updated`,
|
||||||
`owner`.`contact-type` AS `owner-contact-type`,
|
`owner`.`contact-type` AS `owner-contact-type`,
|
||||||
`post`.`causer-id` AS `causer-id`,
|
`post`.`causer-id` AS `causer-id`,
|
||||||
`causer`.`url` AS `causer-link`,
|
`causer`.`url` AS `causer-link`,
|
||||||
|
@ -2059,6 +2124,8 @@ CREATE VIEW `post-view` AS SELECT
|
||||||
`post-question`.`multiple` AS `question-multiple`,
|
`post-question`.`multiple` AS `question-multiple`,
|
||||||
`post-question`.`voters` AS `question-voters`,
|
`post-question`.`voters` AS `question-voters`,
|
||||||
`post-question`.`end-time` AS `question-end-time`,
|
`post-question`.`end-time` AS `question-end-time`,
|
||||||
|
0 AS `has-categories`,
|
||||||
|
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post`.`uri-id`) AS `has-media`,
|
||||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||||
`parent-post`.`network` AS `parent-network`,
|
`parent-post`.`network` AS `parent-network`,
|
||||||
|
@ -2162,6 +2229,9 @@ CREATE VIEW `post-thread-view` AS SELECT
|
||||||
`author`.`network` AS `author-network`,
|
`author`.`network` AS `author-network`,
|
||||||
`author`.`blocked` AS `author-blocked`,
|
`author`.`blocked` AS `author-blocked`,
|
||||||
`author`.`hidden` AS `author-hidden`,
|
`author`.`hidden` AS `author-hidden`,
|
||||||
|
`author`.`updated` AS `author-updated`,
|
||||||
|
`author`.`gsid` AS `author-gsid`,
|
||||||
|
`author`.`uri-id` AS `author-uri-id`,
|
||||||
`post-thread`.`owner-id` AS `owner-id`,
|
`post-thread`.`owner-id` AS `owner-id`,
|
||||||
`owner`.`url` AS `owner-link`,
|
`owner`.`url` AS `owner-link`,
|
||||||
`owner`.`addr` AS `owner-addr`,
|
`owner`.`addr` AS `owner-addr`,
|
||||||
|
@ -2171,6 +2241,7 @@ CREATE VIEW `post-thread-view` AS SELECT
|
||||||
`owner`.`network` AS `owner-network`,
|
`owner`.`network` AS `owner-network`,
|
||||||
`owner`.`blocked` AS `owner-blocked`,
|
`owner`.`blocked` AS `owner-blocked`,
|
||||||
`owner`.`hidden` AS `owner-hidden`,
|
`owner`.`hidden` AS `owner-hidden`,
|
||||||
|
`owner`.`updated` AS `owner-updated`,
|
||||||
`owner`.`contact-type` AS `owner-contact-type`,
|
`owner`.`contact-type` AS `owner-contact-type`,
|
||||||
`post-thread`.`causer-id` AS `causer-id`,
|
`post-thread`.`causer-id` AS `causer-id`,
|
||||||
`causer`.`url` AS `causer-link`,
|
`causer`.`url` AS `causer-link`,
|
||||||
|
@ -2186,6 +2257,8 @@ CREATE VIEW `post-thread-view` AS SELECT
|
||||||
`post-question`.`multiple` AS `question-multiple`,
|
`post-question`.`multiple` AS `question-multiple`,
|
||||||
`post-question`.`voters` AS `question-voters`,
|
`post-question`.`voters` AS `question-voters`,
|
||||||
`post-question`.`end-time` AS `question-end-time`,
|
`post-question`.`end-time` AS `question-end-time`,
|
||||||
|
0 AS `has-categories`,
|
||||||
|
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread`.`uri-id`) AS `has-media`,
|
||||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||||
`parent-post`.`network` AS `parent-network`,
|
`parent-post`.`network` AS `parent-network`,
|
||||||
|
@ -2234,9 +2307,14 @@ CREATE VIEW `collection-view` AS SELECT
|
||||||
`post-collection`.`type` AS `type`,
|
`post-collection`.`type` AS `type`,
|
||||||
`post`.`author-id` AS `cid`,
|
`post`.`author-id` AS `cid`,
|
||||||
`post`.`received` AS `received`,
|
`post`.`received` AS `received`,
|
||||||
`post`.`created` AS `created`
|
`post`.`created` AS `created`,
|
||||||
|
`post-thread`.`commented` AS `commented`,
|
||||||
|
`post`.`thr-parent-id` AS `thr-parent-id`,
|
||||||
|
`post`.`author-id` AS `author-id`,
|
||||||
|
`post`.`gravity` AS `gravity`
|
||||||
FROM `post-collection`
|
FROM `post-collection`
|
||||||
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`;
|
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`
|
||||||
|
INNER JOIN `post-thread` ON `post-thread`.`uri-id` = `post`.`parent-uri-id`;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- VIEW tag-view
|
-- VIEW tag-view
|
||||||
|
|
|
@ -50,7 +50,9 @@ Database Tables
|
||||||
| [post-category](help/database/db_post-category) | post relation to categories |
|
| [post-category](help/database/db_post-category) | post relation to categories |
|
||||||
| [post-collection](help/database/db_post-collection) | Collection of posts |
|
| [post-collection](help/database/db_post-collection) | Collection of posts |
|
||||||
| [post-content](help/database/db_post-content) | Content for all posts |
|
| [post-content](help/database/db_post-content) | Content for all posts |
|
||||||
|
| [post-delivery](help/database/db_post-delivery) | Delivery data for posts for the batch processing |
|
||||||
| [post-delivery-data](help/database/db_post-delivery-data) | Delivery data for items |
|
| [post-delivery-data](help/database/db_post-delivery-data) | Delivery data for items |
|
||||||
|
| [post-history](help/database/db_post-history) | Post history |
|
||||||
| [post-link](help/database/db_post-link) | Post related external links |
|
| [post-link](help/database/db_post-link) | Post related external links |
|
||||||
| [post-media](help/database/db_post-media) | Attached media |
|
| [post-media](help/database/db_post-media) | Attached media |
|
||||||
| [post-question](help/database/db_post-question) | Question |
|
| [post-question](help/database/db_post-question) | Question |
|
||||||
|
|
|
@ -9,6 +9,7 @@ Fields
|
||||||
| Field | Description | Type | Null | Key | Default | Extra |
|
| Field | Description | Type | Null | Key | Default | Extra |
|
||||||
| -------- | ------------------------------------ | -------------- | ---- | --- | ------------------- | ----- |
|
| -------- | ------------------------------------ | -------------- | ---- | --- | ------------------- | ----- |
|
||||||
| url | URL of the inbox | varbinary(255) | NO | PRI | NULL | |
|
| url | URL of the inbox | varbinary(255) | NO | PRI | NULL | |
|
||||||
|
| uri-id | Item-uri id of inbox url | int unsigned | YES | | NULL | |
|
||||||
| created | Creation date of this entry | datetime | NO | | 0001-01-01 00:00:00 | |
|
| created | Creation date of this entry | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||||
| success | Date of the last successful delivery | datetime | NO | | 0001-01-01 00:00:00 | |
|
| success | Date of the last successful delivery | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||||
| failure | Date of the last failed delivery | datetime | NO | | 0001-01-01 00:00:00 | |
|
| failure | Date of the last failed delivery | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||||
|
@ -22,6 +23,13 @@ Indexes
|
||||||
| Name | Fields |
|
| Name | Fields |
|
||||||
| ------- | ------ |
|
| ------- | ------ |
|
||||||
| PRIMARY | url |
|
| PRIMARY | url |
|
||||||
|
| uri-id | uri-id |
|
||||||
|
|
||||||
|
Foreign Keys
|
||||||
|
------------
|
||||||
|
|
||||||
|
| Field | Target Table | Target Field |
|
||||||
|
|-------|--------------|--------------|
|
||||||
|
| uri-id | [item-uri](help/database/db_item-uri) | id |
|
||||||
|
|
||||||
Return to [database documentation](help/database)
|
Return to [database documentation](help/database)
|
||||||
|
|
|
@ -17,10 +17,10 @@ Indexes
|
||||||
------------
|
------------
|
||||||
|
|
||||||
| Name | Fields |
|
| Name | Fields |
|
||||||
| ------- | ---------------------- |
|
| ---------- | ---------------------- |
|
||||||
| PRIMARY | uri-id, uid, type, tid |
|
| PRIMARY | uri-id, uid, type, tid |
|
||||||
| uri-id | tid |
|
| tid | tid |
|
||||||
| uid | uid |
|
| uid_uri-id | uid, uri-id |
|
||||||
|
|
||||||
Foreign Keys
|
Foreign Keys
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
Table post-delivery
|
||||||
|
===========
|
||||||
|
|
||||||
|
Delivery data for posts for the batch processing
|
||||||
|
|
||||||
|
Fields
|
||||||
|
------
|
||||||
|
|
||||||
|
| Field | Description | Type | Null | Key | Default | Extra |
|
||||||
|
| --------- | --------------------------------------------------------- | ------------------ | ---- | --- | ------------------- | ----- |
|
||||||
|
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
|
||||||
|
| inbox-id | Item-uri id of inbox url | int unsigned | NO | PRI | NULL | |
|
||||||
|
| uid | Delivering user | mediumint unsigned | YES | | NULL | |
|
||||||
|
| created | | datetime | YES | | 0001-01-01 00:00:00 | |
|
||||||
|
| command | | varbinary(32) | YES | | NULL | |
|
||||||
|
| failed | Number of times the delivery has failed | tinyint | YES | | 0 | |
|
||||||
|
| receivers | JSON encoded array with the receiving contacts | mediumtext | YES | | NULL | |
|
||||||
|
|
||||||
|
Indexes
|
||||||
|
------------
|
||||||
|
|
||||||
|
| Name | Fields |
|
||||||
|
| ---------------- | ----------------- |
|
||||||
|
| PRIMARY | uri-id, inbox-id |
|
||||||
|
| inbox-id_created | inbox-id, created |
|
||||||
|
| uid | uid |
|
||||||
|
|
||||||
|
Foreign Keys
|
||||||
|
------------
|
||||||
|
|
||||||
|
| Field | Target Table | Target Field |
|
||||||
|
|-------|--------------|--------------|
|
||||||
|
| uri-id | [item-uri](help/database/db_item-uri) | id |
|
||||||
|
| inbox-id | [item-uri](help/database/db_item-uri) | id |
|
||||||
|
| uid | [user](help/database/db_user) | uid |
|
||||||
|
|
||||||
|
Return to [database documentation](help/database)
|
|
@ -0,0 +1,44 @@
|
||||||
|
Table post-history
|
||||||
|
===========
|
||||||
|
|
||||||
|
Post history
|
||||||
|
|
||||||
|
Fields
|
||||||
|
------
|
||||||
|
|
||||||
|
| Field | Description | Type | Null | Key | Default | Extra |
|
||||||
|
| --------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------ | ---- | --- | ------------------- | ----- |
|
||||||
|
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
|
||||||
|
| edited | Date of edit | datetime | NO | PRI | 0001-01-01 00:00:00 | |
|
||||||
|
| title | item title | varchar(255) | NO | | | |
|
||||||
|
| content-warning | | varchar(255) | NO | | | |
|
||||||
|
| body | item body content | mediumtext | YES | | NULL | |
|
||||||
|
| raw-body | Body without embedded media links | mediumtext | YES | | NULL | |
|
||||||
|
| location | text location where this item originated | varchar(255) | NO | | | |
|
||||||
|
| coord | longitude/latitude pair representing location where this item originated | varchar(255) | NO | | | |
|
||||||
|
| language | Language information about this post | text | YES | | NULL | |
|
||||||
|
| app | application which generated this item | varchar(255) | NO | | | |
|
||||||
|
| rendered-hash | | varchar(32) | NO | | | |
|
||||||
|
| rendered-html | item.body converted to html | mediumtext | YES | | NULL | |
|
||||||
|
| object-type | ActivityStreams object type | varchar(100) | NO | | | |
|
||||||
|
| object | JSON encoded object structure unless it is an implied object (normal post) | text | YES | | NULL | |
|
||||||
|
| target-type | ActivityStreams target type if applicable (URI) | varchar(100) | NO | | | |
|
||||||
|
| target | JSON encoded target structure if used | text | YES | | NULL | |
|
||||||
|
| resource-id | Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type | varchar(32) | NO | | | |
|
||||||
|
| plink | permalink or URL to a displayable copy of the message at its source | varchar(255) | NO | | | |
|
||||||
|
|
||||||
|
Indexes
|
||||||
|
------------
|
||||||
|
|
||||||
|
| Name | Fields |
|
||||||
|
| ------- | -------------- |
|
||||||
|
| PRIMARY | uri-id, edited |
|
||||||
|
|
||||||
|
Foreign Keys
|
||||||
|
------------
|
||||||
|
|
||||||
|
| Field | Target Table | Target Field |
|
||||||
|
|-------|--------------|--------------|
|
||||||
|
| uri-id | [item-uri](help/database/db_item-uri) | id |
|
||||||
|
|
||||||
|
Return to [database documentation](help/database)
|
|
@ -35,6 +35,7 @@ Indexes
|
||||||
| ---------- | ------------------- |
|
| ---------- | ------------------- |
|
||||||
| PRIMARY | id |
|
| PRIMARY | id |
|
||||||
| uri-id-url | UNIQUE, uri-id, url |
|
| uri-id-url | UNIQUE, uri-id, url |
|
||||||
|
| uri-id-id | uri-id, id |
|
||||||
|
|
||||||
Foreign Keys
|
Foreign Keys
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -110,7 +110,13 @@ function display_init(App $a)
|
||||||
$item = $parent ?: $item;
|
$item = $parent ?: $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
DI::page()['aside'] = Widget\VCard::getHTML(display_fetchauthor($item));
|
$author = display_fetchauthor($item);
|
||||||
|
|
||||||
|
if (\Friendica\Util\Network::isLocalLink($author['url'])) {
|
||||||
|
\Friendica\Model\Profile::load(DI::app(), $author['nick'], false);
|
||||||
|
} else {
|
||||||
|
DI::page()['aside'] = Widget\VCard::getHTML($author);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function display_fetchauthor($item)
|
function display_fetchauthor($item)
|
||||||
|
|
|
@ -39,11 +39,11 @@ use Friendica\Util\Strings;
|
||||||
function fbrowser_content(App $a)
|
function fbrowser_content(App $a)
|
||||||
{
|
{
|
||||||
if (!local_user()) {
|
if (!local_user()) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DI::args()->getArgc() == 1) {
|
if (DI::args()->getArgc() == 1) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed to match the correct template in a module that uses a different theme than the user/site/default
|
// Needed to match the correct template in a module that uses a different theme than the user/site/default
|
||||||
|
|
15
mod/item.php
15
mod/item.php
|
@ -621,6 +621,11 @@ function item_post(App $a) {
|
||||||
$datarray["id"] = -1;
|
$datarray["id"] = -1;
|
||||||
$datarray["uri-id"] = -1;
|
$datarray["uri-id"] = -1;
|
||||||
$datarray["author-network"] = Protocol::DFRN;
|
$datarray["author-network"] = Protocol::DFRN;
|
||||||
|
$datarray["author-updated"] = '';
|
||||||
|
$datarray["author-gsid"] = 0;
|
||||||
|
$datarray["author-uri-id"] = ItemURI::getIdByURI($datarray["author-link"]);
|
||||||
|
$datarray["owner-updated"] = '';
|
||||||
|
$datarray["has-media"] = false;
|
||||||
|
|
||||||
$o = DI::conversation()->create([array_merge($contact_record, $datarray)], 'search', false, true);
|
$o = DI::conversation()->create([array_merge($contact_record, $datarray)], 'search', false, true);
|
||||||
|
|
||||||
|
@ -663,21 +668,17 @@ function item_post(App $a) {
|
||||||
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
|
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
|
||||||
|
|
||||||
if ($orig_post) {
|
if ($orig_post) {
|
||||||
// Fill the cache field
|
|
||||||
// This could be done in Item::update as well - but we have to check for the existance of some fields.
|
|
||||||
Item::putInCache($datarray);
|
|
||||||
|
|
||||||
$fields = [
|
$fields = [
|
||||||
'title' => $datarray['title'],
|
'title' => $datarray['title'],
|
||||||
'body' => $datarray['body'],
|
'body' => $datarray['body'],
|
||||||
'attach' => $datarray['attach'],
|
'attach' => $datarray['attach'],
|
||||||
'file' => $datarray['file'],
|
'file' => $datarray['file'],
|
||||||
'rendered-html' => $datarray['rendered-html'],
|
|
||||||
'rendered-hash' => $datarray['rendered-hash'],
|
|
||||||
'edited' => DateTimeFormat::utcNow(),
|
'edited' => DateTimeFormat::utcNow(),
|
||||||
'changed' => DateTimeFormat::utcNow()];
|
'changed' => DateTimeFormat::utcNow()
|
||||||
|
];
|
||||||
|
|
||||||
Item::update($fields, ['id' => $post_id]);
|
Item::update($fields, ['id' => $post_id]);
|
||||||
|
Item::updateDisplayCache($datarray['uri-id']);
|
||||||
|
|
||||||
if ($return_path) {
|
if ($return_path) {
|
||||||
DI::baseUrl()->redirect($return_path);
|
DI::baseUrl()->redirect($return_path);
|
||||||
|
|
|
@ -65,7 +65,7 @@ function photos_init(App $a) {
|
||||||
|
|
||||||
if (DI::args()->getArgc() > 1) {
|
if (DI::args()->getArgc() > 1) {
|
||||||
$owner = User::getOwnerDataByNick(DI::args()->getArgv()[1]);
|
$owner = User::getOwnerDataByNick(DI::args()->getArgv()[1]);
|
||||||
if (!$owner) {
|
if (empty($owner) || $owner['account_removed']) {
|
||||||
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ function photos_post(App $a)
|
||||||
|
|
||||||
if (!$can_post) {
|
if (!$can_post) {
|
||||||
notice(DI::l10n()->t('Permission denied.'));
|
notice(DI::l10n()->t('Permission denied.'));
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$owner_record = User::getOwnerDataById($page_owner_uid);
|
$owner_record = User::getOwnerDataById($page_owner_uid);
|
||||||
|
@ -166,7 +166,7 @@ function photos_post(App $a)
|
||||||
if (!$owner_record) {
|
if (!$owner_record) {
|
||||||
notice(DI::l10n()->t('Contact information unavailable'));
|
notice(DI::l10n()->t('Contact information unavailable'));
|
||||||
DI::logger()->info('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
|
DI::logger()->info('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$aclFormatter = DI::aclFormatter();
|
$aclFormatter = DI::aclFormatter();
|
||||||
|
@ -1257,14 +1257,12 @@ function photos_content(App $a)
|
||||||
$tags = null;
|
$tags = null;
|
||||||
|
|
||||||
if (!empty($link_item['id'])) {
|
if (!empty($link_item['id'])) {
|
||||||
$tag_text = Tag::getCSVByURIId($link_item['uri-id']);
|
|
||||||
$arr = explode(',', $tag_text);
|
|
||||||
// parse tags and add links
|
// parse tags and add links
|
||||||
$tag_arr = [];
|
$tag_arr = [];
|
||||||
foreach ($arr as $tag) {
|
foreach (Tag::getByURIId($link_item['uri-id']) as $tag) {
|
||||||
$tag_arr[] = [
|
$tag_arr[] = [
|
||||||
'name' => BBCode::convert($tag),
|
'name' => $tag['name'],
|
||||||
'removeurl' => '/tagrm/' . $link_item['id'] . '/' . bin2hex($tag)
|
'removeurl' => '/tagrm/' . $link_item['id'] . '/' . bin2hex($tag['name'])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
|
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
use Friendica\App;
|
use Friendica\App;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Protocol;
|
use Friendica\Core\Protocol;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
|
@ -38,7 +39,7 @@ function hub_return($valid, $body)
|
||||||
} else {
|
} else {
|
||||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// when receiving an XML feed, always return OK
|
// when receiving an XML feed, always return OK
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
use Friendica\App;
|
use Friendica\App;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\PushSubscriber;
|
use Friendica\Model\PushSubscriber;
|
||||||
|
@ -142,5 +143,5 @@ function pubsubhubbub_init(App $a) {
|
||||||
|
|
||||||
throw new \Friendica\Network\HTTPException\AcceptedException();
|
throw new \Friendica\Network\HTTPException\AcceptedException();
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,25 +23,16 @@ use Friendica\App;
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
use Friendica\Content\Feature;
|
use Friendica\Content\Feature;
|
||||||
use Friendica\Content\Nav;
|
use Friendica\Content\Nav;
|
||||||
use Friendica\Core\ACL;
|
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
use Friendica\Core\Worker;
|
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Group;
|
|
||||||
use Friendica\Model\Item;
|
use Friendica\Model\Item;
|
||||||
use Friendica\Model\Notification;
|
|
||||||
use Friendica\Model\Profile;
|
|
||||||
use Friendica\Model\User;
|
use Friendica\Model\User;
|
||||||
use Friendica\Model\Verb;
|
|
||||||
use Friendica\Module\BaseSettings;
|
use Friendica\Module\BaseSettings;
|
||||||
use Friendica\Module\Security\Login;
|
use Friendica\Module\Security\Login;
|
||||||
use Friendica\Protocol\Activity;
|
|
||||||
use Friendica\Protocol\Email;
|
use Friendica\Protocol\Email;
|
||||||
use Friendica\Util\Temporal;
|
|
||||||
use Friendica\Worker\Delivery;
|
|
||||||
|
|
||||||
function settings_init(App $a)
|
function settings_init(App $a)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
use Friendica\App;
|
use Friendica\App;
|
||||||
use Friendica\Content\Text\BBCode;
|
use Friendica\Content\Text\BBCode;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Item;
|
use Friendica\Model\Item;
|
||||||
|
@ -30,7 +31,7 @@ function share_init(App $a) {
|
||||||
$post_id = ((DI::args()->getArgc() > 1) ? intval(DI::args()->getArgv()[1]) : 0);
|
$post_id = ((DI::args()->getArgc() > 1) ? intval(DI::args()->getArgv()[1]) : 0);
|
||||||
|
|
||||||
if (!$post_id || !local_user()) {
|
if (!$post_id || !local_user()) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields = ['private', 'body', 'author-name', 'author-link', 'author-avatar',
|
$fields = ['private', 'body', 'author-name', 'author-link', 'author-avatar',
|
||||||
|
@ -38,7 +39,7 @@ function share_init(App $a) {
|
||||||
$item = Post::selectFirst($fields, ['id' => $post_id]);
|
$item = Post::selectFirst($fields, ['id' => $post_id]);
|
||||||
|
|
||||||
if (!DBA::isResult($item) || $item['private'] == Item::PRIVATE) {
|
if (!DBA::isResult($item) || $item['private'] == Item::PRIVATE) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($item['body'], "[/share]") !== false) {
|
if (strpos($item['body'], "[/share]") !== false) {
|
||||||
|
@ -56,5 +57,5 @@ function share_init(App $a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
echo $o;
|
echo $o;
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,5 @@ EOT;
|
||||||
$post = Post::selectFirst(['uri-id', 'uid'], ['id' => $post_id]);
|
$post = Post::selectFirst(['uri-id', 'uid'], ['id' => $post_id]);
|
||||||
|
|
||||||
Worker::add(PRIORITY_HIGH, "Notifier", Delivery::POST, $post['uri-id'], $post['uid']);
|
Worker::add(PRIORITY_HIGH, "Notifier", Delivery::POST, $post['uri-id'], $post['uid']);
|
||||||
|
System::exit();
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,14 +67,14 @@ function wall_attach_post(App $a) {
|
||||||
System::jsonExit(['error' => DI::l10n()->t('Permission denied.')]);
|
System::jsonExit(['error' => DI::l10n()->t('Permission denied.')]);
|
||||||
}
|
}
|
||||||
notice(DI::l10n()->t('Permission denied.') . EOL );
|
notice(DI::l10n()->t('Permission denied.') . EOL );
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($_FILES['userfile'])) {
|
if (empty($_FILES['userfile'])) {
|
||||||
if ($r_json) {
|
if ($r_json) {
|
||||||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$src = $_FILES['userfile']['tmp_name'];
|
$src = $_FILES['userfile']['tmp_name'];
|
||||||
|
@ -97,7 +97,7 @@ function wall_attach_post(App $a) {
|
||||||
} else {
|
} else {
|
||||||
notice($msg);
|
notice($msg);
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($maxfilesize && $filesize > $maxfilesize) {
|
if ($maxfilesize && $filesize > $maxfilesize) {
|
||||||
|
@ -108,7 +108,7 @@ function wall_attach_post(App $a) {
|
||||||
} else {
|
} else {
|
||||||
echo $msg . EOL;
|
echo $msg . EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$newid = Attach::storeFile($src, $page_owner_uid, $filename, '<' . $page_owner_cid . '>');
|
$newid = Attach::storeFile($src, $page_owner_uid, $filename, '<' . $page_owner_cid . '>');
|
||||||
|
@ -122,7 +122,7 @@ function wall_attach_post(App $a) {
|
||||||
} else {
|
} else {
|
||||||
echo $msg . EOL;
|
echo $msg . EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r_json) {
|
if ($r_json) {
|
||||||
|
@ -132,7 +132,6 @@ function wall_attach_post(App $a) {
|
||||||
$lf = "\n";
|
$lf = "\n";
|
||||||
|
|
||||||
echo $lf . $lf . '[attachment]' . $newid . '[/attachment]' . $lf;
|
echo $lf . $lf . '[attachment]' . $newid . '[/attachment]' . $lf;
|
||||||
|
System::exit();
|
||||||
exit();
|
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,14 +89,14 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
System::jsonExit(['error' => DI::l10n()->t('Permission denied.')]);
|
System::jsonExit(['error' => DI::l10n()->t('Permission denied.')]);
|
||||||
}
|
}
|
||||||
notice(DI::l10n()->t('Permission denied.'));
|
notice(DI::l10n()->t('Permission denied.'));
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($_FILES['userfile']) && empty($_FILES['media'])) {
|
if (empty($_FILES['userfile']) && empty($_FILES['media'])) {
|
||||||
if ($r_json) {
|
if ($r_json) {
|
||||||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$src = '';
|
$src = '';
|
||||||
|
@ -148,7 +148,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||||
}
|
}
|
||||||
notice(DI::l10n()->t('Invalid request.'));
|
notice(DI::l10n()->t('Invalid request.'));
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
||||||
|
@ -167,7 +167,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
} else {
|
} else {
|
||||||
echo $msg. EOL;
|
echo $msg. EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$Image->orient($src);
|
$Image->orient($src);
|
||||||
|
@ -205,7 +205,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
} else {
|
} else {
|
||||||
echo $msg. EOL;
|
echo $msg. EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
} else {
|
} else {
|
||||||
echo $msg. EOL;
|
echo $msg. EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($width > 640 || $height > 640) {
|
if ($width > 640 || $height > 640) {
|
||||||
|
@ -281,6 +281,6 @@ function wall_upload_post(App $a, $desktopmode = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id . '][img]' . DI::baseUrl() . "/photo/{$resource_id}-{$smallest}.".$Image->getExt()."[/img][/url]\n\n";
|
echo "\n\n" . '[url=' . DI::baseUrl() . '/photos/' . $page_owner_nick . '/image/' . $resource_id . '][img]' . DI::baseUrl() . "/photo/{$resource_id}-{$smallest}.".$Image->getExt()."[/img][/url]\n\n";
|
||||||
exit();
|
System::exit();
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
}
|
}
|
||||||
|
|
|
@ -472,7 +472,7 @@ class App
|
||||||
// Allow folks to override user themes and always use their own on their own site.
|
// Allow folks to override user themes and always use their own on their own site.
|
||||||
// This works only if the user is on the same server
|
// This works only if the user is on the same server
|
||||||
$user = $this->database->selectFirst('user', ['theme'], ['uid' => $this->profile_owner]);
|
$user = $this->database->selectFirst('user', ['theme'], ['uid' => $this->profile_owner]);
|
||||||
if ($this->database->isResult($user) && !$this->pConfig->get(local_user(), 'system', 'always_my_theme')) {
|
if ($this->database->isResult($user) && !local_user()) {
|
||||||
$page_theme = $user['theme'];
|
$page_theme = $user['theme'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,7 +504,7 @@ class App
|
||||||
if (!empty($this->profile_owner) && ($this->profile_owner != local_user())) {
|
if (!empty($this->profile_owner) && ($this->profile_owner != local_user())) {
|
||||||
// Allow folks to override user themes and always use their own on their own site.
|
// Allow folks to override user themes and always use their own on their own site.
|
||||||
// This works only if the user is on the same server
|
// This works only if the user is on the same server
|
||||||
if (!$this->pConfig->get(local_user(), 'system', 'always_my_theme')) {
|
if (!local_user()) {
|
||||||
$page_mobile_theme = $this->pConfig->get($this->profile_owner, 'system', 'mobile-theme');
|
$page_mobile_theme = $this->pConfig->get($this->profile_owner, 'system', 'mobile-theme');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,6 +576,7 @@ class App
|
||||||
$this->profiler->set(microtime(true), 'classinit');
|
$this->profiler->set(microtime(true), 'classinit');
|
||||||
|
|
||||||
$moduleName = $this->args->getModuleName();
|
$moduleName = $this->args->getModuleName();
|
||||||
|
$page->setLogging($this->args->getCommand(), $this->args->getMethod());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Missing DB connection: ERROR
|
// Missing DB connection: ERROR
|
||||||
|
@ -718,6 +719,7 @@ class App
|
||||||
} catch (HTTPException $e) {
|
} catch (HTTPException $e) {
|
||||||
(new ModuleHTTPException())->rawContent($e);
|
(new ModuleHTTPException())->rawContent($e);
|
||||||
}
|
}
|
||||||
|
$page->logRuntime($this->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,6 +30,7 @@ use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
use Friendica\Core\L10n;
|
use Friendica\Core\L10n;
|
||||||
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
use Friendica\Core\Theme;
|
use Friendica\Core\Theme;
|
||||||
|
@ -78,14 +79,37 @@ class Page implements ArrayAccess
|
||||||
*/
|
*/
|
||||||
private $basePath;
|
private $basePath;
|
||||||
|
|
||||||
|
private $timestamp = 0;
|
||||||
|
private $command = '';
|
||||||
|
private $method = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $basepath The Page basepath
|
* @param string $basepath The Page basepath
|
||||||
*/
|
*/
|
||||||
public function __construct(string $basepath)
|
public function __construct(string $basepath)
|
||||||
{
|
{
|
||||||
|
$this->timestamp = microtime(true);
|
||||||
$this->basePath = $basepath;
|
$this->basePath = $basepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLogging(string $command, string $method)
|
||||||
|
{
|
||||||
|
$this->command = $command;
|
||||||
|
$this->method = $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logRuntime(IManageConfigValues $config)
|
||||||
|
{
|
||||||
|
if (in_array($this->command, $config->get('system', 'runtime_ignore'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$runtime = number_format(microtime(true) - $this->timestamp, 3);
|
||||||
|
if ($runtime > $config->get('system', 'runtime_loglimit')) {
|
||||||
|
Logger::debug('Runtime', ['method' => $this->method, 'command' => $this->command, 'runtime' => $runtime]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether a offset exists
|
* Whether a offset exists
|
||||||
*
|
*
|
||||||
|
@ -421,6 +445,9 @@ class Page implements ArrayAccess
|
||||||
{
|
{
|
||||||
$moduleName = $args->getModuleName();
|
$moduleName = $args->getModuleName();
|
||||||
|
|
||||||
|
$this->command = $moduleName;
|
||||||
|
$this->method = $args->getMethod();
|
||||||
|
|
||||||
/* Create the page content.
|
/* Create the page content.
|
||||||
* Calls all hooks which are including content operations
|
* Calls all hooks which are including content operations
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Console;
|
||||||
|
|
||||||
|
use Friendica\App\BaseURL;
|
||||||
|
use Friendica\Contact\Avatar;
|
||||||
|
use Friendica\Core\L10n;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\Photo;
|
||||||
|
use Friendica\Util\Images;
|
||||||
|
use Friendica\Object\Image;
|
||||||
|
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
|
use Friendica\Core\Protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tool to move cached avatars to the avatar file cache.
|
||||||
|
*/
|
||||||
|
class MoveToAvatarCache extends \Asika\SimpleConsole\Console
|
||||||
|
{
|
||||||
|
protected $helpOptions = ['h', 'help', '?'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $dba Friendica\Database\Database
|
||||||
|
*/
|
||||||
|
private $dba;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $baseurl Friendica\App\BaseURL
|
||||||
|
*/
|
||||||
|
private $baseurl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var L10n
|
||||||
|
*/
|
||||||
|
private $l10n;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IManageConfigValues
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
protected function getHelp()
|
||||||
|
{
|
||||||
|
$help = <<<HELP
|
||||||
|
console movetoavatarcache - Move all cached avatars to the file based avatar cache
|
||||||
|
Synopsis
|
||||||
|
bin/console movetoavatarcache
|
||||||
|
|
||||||
|
Description
|
||||||
|
bin/console movetoavatarcache
|
||||||
|
Move all cached avatars to the file based avatar cache
|
||||||
|
|
||||||
|
Options
|
||||||
|
-h|--help|-? Show help information
|
||||||
|
HELP;
|
||||||
|
return $help;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(\Friendica\Database\Database $dba, BaseURL $baseurl, L10n $l10n, IManageConfigValues $config, array $argv = null)
|
||||||
|
{
|
||||||
|
parent::__construct($argv);
|
||||||
|
|
||||||
|
$this->dba = $dba;
|
||||||
|
$this->baseurl = $baseurl;
|
||||||
|
$this->l10n = $l10n;
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doExecute()
|
||||||
|
{
|
||||||
|
if (!$this->config->get('system', 'avatar_cache')) {
|
||||||
|
$this->err($this->l10n->t('The avatar cache needs to be enabled to use this command.'));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = ['id', 'avatar', 'photo', 'thumb', 'micro', 'uri-id', 'url', 'avatar', 'network'];
|
||||||
|
$condition = ["NOT `self` AND `avatar` != ? AND `photo` LIKE ? AND `uid` = ? AND `uri-id` != ? AND NOT `uri-id` IS NULL AND NOT `network` IN (?, ?)",
|
||||||
|
'', $this->baseurl->get() . '/photo/%', 0, 0, Protocol::MAIL, Protocol::FEED];
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$total = $this->dba->count('contact', $condition);
|
||||||
|
$contacts = $this->dba->select('contact', $fields, $condition, ['order' => ['id']]);
|
||||||
|
while ($contact = $this->dba->fetch($contacts)) {
|
||||||
|
if (Contact::isLocal($contact['url'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->out(++$count . '/' . $total . "\t" . $contact['id'] . "\t" . $contact['url'] . "\t", false);
|
||||||
|
$resourceid = Photo::ridFromURI($contact['photo']);
|
||||||
|
if (empty($resourceid)) {
|
||||||
|
$this->out($this->l10n->t('no resource in photo %s', $contact['photo']) . ' ', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->storeAvatar($resourceid, $contact, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$totals = $this->dba->p("SELECT COUNT(DISTINCT(`resource-id`)) AS `total` FROM `photo` WHERE `contact-id` != ? AND `photo-type` = ?;", 0, Photo::CONTACT_AVATAR);
|
||||||
|
$total = $this->dba->fetch($totals)['total'] ?? 0;
|
||||||
|
$photos = $this->dba->p("SELECT `resource-id`, MAX(`contact-id`) AS `contact-id` FROM `photo` WHERE `contact-id` != ? AND `photo-type` = ? GROUP BY `resource-id`;", 0, Photo::CONTACT_AVATAR);
|
||||||
|
while ($photo = $this->dba->fetch($photos)) {
|
||||||
|
$contact = Contact::getById($photo['contact-id'], $fields);
|
||||||
|
if (empty($contact) || in_array($contact['network'], [Protocol::MAIL, Protocol::FEED]) || Contact::isLocal($contact['url'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->out(++$count . '/' . $total . "\t" . $contact['id'] . "\t" . $contact['url'] . "\t", false);
|
||||||
|
$this->storeAvatar($photo['resource-id'], $contact, true);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function storeAvatar(string $resourceid, array $contact, bool $quit_on_invalid)
|
||||||
|
{
|
||||||
|
$valid = !empty($resourceid);
|
||||||
|
if ($valid) {
|
||||||
|
$this->out('1', false);
|
||||||
|
$photo = Photo::selectFirst([], ['resource-id' => $resourceid], ['order' => ['scale']]);
|
||||||
|
if (empty($photo)) {
|
||||||
|
$this->out(' ' . $this->l10n->t('no photo with id %s', $resourceid) . ' ', false);
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
$this->out('2', false);
|
||||||
|
$imgdata = Photo::getImageDataForPhoto($photo);
|
||||||
|
if (empty($imgdata)) {
|
||||||
|
$this->out(' ' . $this->l10n->t('no image data for photo with id %s', $resourceid) . ' ', false);
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
$this->out('3', false);
|
||||||
|
$image = new Image($imgdata, Images::getMimeTypeByData($imgdata));
|
||||||
|
if (!$image->isValid()) {
|
||||||
|
$this->out(' ' . $this->l10n->t('invalid image for id %s', $resourceid) . ' ', false);
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
$this->out('4', false);
|
||||||
|
$fields = Avatar::storeAvatarByImage($contact, $image);
|
||||||
|
} else {
|
||||||
|
$fields = ['photo' => '', 'thumb' => '', 'micro' => ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($quit_on_invalid && $fields['photo'] == '') {
|
||||||
|
$this->out(' ' . $this->l10n->t('Quit on invalid photo %s', $contact['avatar']));
|
||||||
|
Photo::delete(['resource-id' => $resourceid]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out('5', false);
|
||||||
|
Contact::update($fields, ['uri-id' => $contact['uri-id']]);
|
||||||
|
$this->out('6', false);
|
||||||
|
Photo::delete(['resource-id' => $resourceid]);
|
||||||
|
$this->out(' ' . $fields['photo']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Console;
|
||||||
|
|
||||||
|
use Asika\SimpleConsole\Console;
|
||||||
|
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
|
use Friendica\Core\Worker;
|
||||||
|
use Friendica\Util\Strings;
|
||||||
|
use Friendica\Worker\Delivery;
|
||||||
|
|
||||||
|
class Relocate extends Console
|
||||||
|
{
|
||||||
|
protected $helpOptions = ['h', 'help', '?'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var IManageConfigValues
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
/**
|
||||||
|
* @var \Friendica\App\BaseURL
|
||||||
|
*/
|
||||||
|
private $baseUrl;
|
||||||
|
/**
|
||||||
|
* @var \Friendica\Database\Database
|
||||||
|
*/
|
||||||
|
private $database;
|
||||||
|
|
||||||
|
protected function getHelp()
|
||||||
|
{
|
||||||
|
$help = <<<HELP
|
||||||
|
console relocate - Update the node base URL
|
||||||
|
Usage
|
||||||
|
bin/console relocate <new base URL> [-h|--help|-?] [-v]
|
||||||
|
|
||||||
|
Description
|
||||||
|
Warning! Advanced function. Could make this server unreachable.
|
||||||
|
|
||||||
|
Change the base URL for this server. Sends relocation message to all the Friendica and Diaspora* contacts of all local users.
|
||||||
|
This process updates all the database fields that may contain a URL pointing at the current domain, as a result it takes
|
||||||
|
a while and the node will be in maintenance mode for the whole duration.
|
||||||
|
|
||||||
|
Options
|
||||||
|
-h|--help|-? Show help information
|
||||||
|
-v Show more debug information.
|
||||||
|
HELP;
|
||||||
|
return $help;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Database\Database $database, IManageConfigValues $config, $argv = null)
|
||||||
|
{
|
||||||
|
parent::__construct($argv);
|
||||||
|
|
||||||
|
$this->baseUrl = $baseUrl;
|
||||||
|
$this->database = $database;
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function doExecute()
|
||||||
|
{
|
||||||
|
if (count($this->args) == 0) {
|
||||||
|
$this->out($this->getHelp());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->args) > 1) {
|
||||||
|
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_url = rtrim($this->getArgument(0), '/');
|
||||||
|
|
||||||
|
$parsed = @parse_url($new_url);
|
||||||
|
if (!is_array($parsed) || empty($parsed['host']) || empty($parsed['scheme'])) {
|
||||||
|
throw new \InvalidArgumentException('Can not parse new base URL. Must have at least <scheme>://<domain>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->out(sprintf('Relocation started from %s to %s. Could take a while to complete.', $this->baseUrl->get(true), $this->getArgument(0)));
|
||||||
|
|
||||||
|
$old_url = $this->baseUrl->get(true);
|
||||||
|
|
||||||
|
// Generate host names for relocation the addresses in the format user@address.tld
|
||||||
|
$new_host = str_replace('http://', '@', Strings::normaliseLink($new_url));
|
||||||
|
$old_host = str_replace('http://', '@', Strings::normaliseLink($old_url));
|
||||||
|
|
||||||
|
$this->out('Entering maintenance mode');
|
||||||
|
$this->config->set('system', 'maintenance', true);
|
||||||
|
$this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!$this->database->transaction()) {
|
||||||
|
throw new \Exception('Unable to start a transaction, please retry later.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update tables
|
||||||
|
$this->out('Updating apcontact table fields');
|
||||||
|
$this->database->replaceInTableFields('apcontact', ['url', 'inbox', 'outbox', 'sharedinbox', 'photo', 'header', 'alias', 'subscribe', 'baseurl'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('apcontact', ['addr'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating contact table fields');
|
||||||
|
$this->database->replaceInTableFields('contact', ['photo', 'thumb', 'micro', 'url', 'alias', 'request', 'batch', 'notify', 'poll', 'subscribe', 'baseurl', 'confirm', 'poco', 'avatar', 'header'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('contact', ['nurl'], Strings::normaliseLink($old_url), Strings::normaliseLink($new_url));
|
||||||
|
$this->database->replaceInTableFields('contact', ['addr'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating conv table fields');
|
||||||
|
$this->database->replaceInTableFields('conv', ['creator', 'recips'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating delayed-post table fields');
|
||||||
|
$this->database->replaceInTableFields('delayed-post', ['uri'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating endpoint table fields');
|
||||||
|
$this->database->replaceInTableFields('endpoint', ['url'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating event table fields');
|
||||||
|
$this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating fcontact table fields');
|
||||||
|
$this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating fsuggest table fields');
|
||||||
|
$this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating gserver table fields');
|
||||||
|
$this->database->replaceInTableFields('gserver', ['url'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('gserver', ['nurl'], Strings::normaliseLink($old_url), Strings::normaliseLink($new_url));
|
||||||
|
|
||||||
|
$this->out('Updating inbox-status table fields');
|
||||||
|
$this->database->replaceInTableFields('inbox-status', ['url'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating item-uri table fields');
|
||||||
|
$this->database->replaceInTableFields('item-uri', ['uri'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating mail table fields');
|
||||||
|
$this->database->replaceInTableFields('mail', ['from-photo', 'from-url', 'uri', 'thr-parent'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('mail', ['parent-uri'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating notify table fields');
|
||||||
|
$this->database->replaceInTableFields('notify', ['url', 'photo', 'link', 'msg', 'name_cache', 'msg_cache'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating profile table fields');
|
||||||
|
$this->database->replaceInTableFields('profile', ['photo', 'thumb'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating post-content table fields');
|
||||||
|
$this->database->replaceInTableFields('post-content', ['body', 'raw-body', 'rendered-html', 'target', 'plink'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('post-content', ['body', 'raw-body', 'rendered-html', 'target'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating post-history table fields');
|
||||||
|
$this->database->replaceInTableFields('post-history', ['body', 'raw-body', 'rendered-html', 'target', 'plink'], $old_url, $new_url);
|
||||||
|
$this->database->replaceInTableFields('post-history', ['body', 'raw-body', 'rendered-html', 'target'], $old_host, $new_host);
|
||||||
|
|
||||||
|
$this->out('Updating post-link table fields');
|
||||||
|
$this->database->replaceInTableFields('post-link', ['url'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating post-media table fields');
|
||||||
|
$this->database->replaceInTableFields('post-media', ['url', 'preview', 'author-url', 'author-image', 'publisher-url', 'publisher-image'], $old_url, $new_url);
|
||||||
|
|
||||||
|
$this->out('Updating tag table fields');
|
||||||
|
$this->database->replaceInTableFields('tag', ['url'], $old_url, $new_url);
|
||||||
|
|
||||||
|
// update config
|
||||||
|
$this->out('Updating config values');
|
||||||
|
$this->config->set('system', 'url', $new_url);
|
||||||
|
$this->baseUrl->saveByURL($new_url);
|
||||||
|
|
||||||
|
$this->database->commit();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->database->rollback();
|
||||||
|
|
||||||
|
$this->out('Process aborted with message: ' . $e->getMessage() . ' thrown in ' . $e->getFile() . ':' . $e->getLine());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} finally {
|
||||||
|
$this->out('Leaving maintenance mode');
|
||||||
|
$this->config->set('system', 'maintenance', false);
|
||||||
|
$this->config->set('system', 'maintenance_reason', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// send relocate
|
||||||
|
$this->out('Schedule relocation messages to remote Friendica and Diaspora hosts');
|
||||||
|
$users = $this->database->selectToArray('user', ['uid'], ['account_removed' => false, 'account_expired' => false]);
|
||||||
|
foreach ($users as $user) {
|
||||||
|
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $user['uid']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,265 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Contact;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Item;
|
||||||
|
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||||
|
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||||
|
use Friendica\Object\Image;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
use Friendica\Util\HTTPSignature;
|
||||||
|
use Friendica\Util\Images;
|
||||||
|
use Friendica\Util\Network;
|
||||||
|
use Friendica\Util\Proxy;
|
||||||
|
use Friendica\Util\Strings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* functions for handling contact avatar caching
|
||||||
|
*/
|
||||||
|
class Avatar
|
||||||
|
{
|
||||||
|
const BASE_PATH = '/avatar/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a field array with locally cached avatar pictures
|
||||||
|
*
|
||||||
|
* @param array $contact Contact array
|
||||||
|
* @param string $avatar Link to avatar picture
|
||||||
|
* @param bool $force force picture update
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function fetchAvatarContact(array $contact, string $avatar, bool $force = false): array
|
||||||
|
{
|
||||||
|
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(), 'photo' => '', 'thumb' => '', 'micro' => ''];
|
||||||
|
|
||||||
|
if (!DI::config()->get('system', 'avatar_cache')) {
|
||||||
|
self::deleteCache($contact);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Network::isLocalLink($avatar) || empty($avatar)) {
|
||||||
|
self::deleteCache($contact);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($avatar != $contact['avatar']) || $force) {
|
||||||
|
self::deleteCache($contact);
|
||||||
|
Logger::debug('Avatar file name changed', ['new' => $avatar, 'old' => $contact['avatar']]);
|
||||||
|
} elseif (self::isCacheFile($contact['photo']) && self::isCacheFile($contact['thumb']) && self::isCacheFile($contact['micro'])) {
|
||||||
|
$fields['photo'] = $contact['photo'];
|
||||||
|
$fields['thumb'] = $contact['thumb'];
|
||||||
|
$fields['micro'] = $contact['micro'];
|
||||||
|
Logger::debug('Using existing cache files', ['uri-id' => $contact['uri-id'], 'fields' => $fields]);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]);
|
||||||
|
|
||||||
|
$img_str = $fetchResult->getBody();
|
||||||
|
if (empty($img_str)) {
|
||||||
|
Logger::debug('Avatar is invalid', ['avatar' => $avatar]);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = new Image($img_str, Images::getMimeTypeByData($img_str));
|
||||||
|
if (!$image->isValid()) {
|
||||||
|
Logger::debug('Avatar picture is invalid', ['avatar' => $avatar]);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = self::getFilename($contact['url']);
|
||||||
|
$timestamp = time();
|
||||||
|
|
||||||
|
$fields['photo'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_SMALL, $timestamp);
|
||||||
|
$fields['thumb'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_THUMB, $timestamp);
|
||||||
|
$fields['micro'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_MICRO, $timestamp);
|
||||||
|
|
||||||
|
Logger::debug('Storing new avatar cache', ['uri-id' => $contact['uri-id'], 'fields' => $fields]);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function storeAvatarByImage(array $contact, Image $image): array
|
||||||
|
{
|
||||||
|
$fields = ['photo' => '', 'thumb' => '', 'micro' => ''];
|
||||||
|
|
||||||
|
if (!DI::config()->get('system', 'avatar_cache')) {
|
||||||
|
self::deleteCache($contact);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Network::isLocalLink($contact['avatar']) || empty($contact['avatar'])) {
|
||||||
|
self::deleteCache($contact);
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = self::getFilename($contact['url']);
|
||||||
|
$timestamp = time();
|
||||||
|
|
||||||
|
$fields['photo'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_SMALL, $timestamp);
|
||||||
|
$fields['thumb'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_THUMB, $timestamp);
|
||||||
|
$fields['micro'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_MICRO, $timestamp);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getFilename(string $url)
|
||||||
|
{
|
||||||
|
$guid = Item::guidFromUri($url, parse_url($url, PHP_URL_HOST));
|
||||||
|
|
||||||
|
return substr($guid, 0, 2) . '/' . substr($guid, 3, 2) . '/' . substr($guid, 5, 3) . '/' .
|
||||||
|
substr($guid, 9, 2) .'/' . substr($guid, 11, 2) . '/' . substr($guid, 13, 4). '/' . substr($guid, 18) . '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function storeAvatarCache(Image $image, string $filename, int $size, int $timestamp): string
|
||||||
|
{
|
||||||
|
$image->scaleDown($size);
|
||||||
|
if (is_null($image) || !$image->isValid()) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = self::BASE_PATH . $filename . $size . '.' . $image->getExt();
|
||||||
|
|
||||||
|
$filepath = DI::basePath() . $path;
|
||||||
|
|
||||||
|
$dirpath = DI::basePath() . self::BASE_PATH;
|
||||||
|
|
||||||
|
DI::profiler()->startRecording('file');
|
||||||
|
|
||||||
|
// Fetch the permission and group ownership of the "avatar" path and apply to all files
|
||||||
|
$dir_perm = fileperms($dirpath) & 0777;
|
||||||
|
$file_perm = fileperms($dirpath) & 0666;
|
||||||
|
$group = filegroup($dirpath);
|
||||||
|
|
||||||
|
// Check directory permissions of all parts of the path
|
||||||
|
foreach (explode('/', dirname($filename)) as $part) {
|
||||||
|
$dirpath .= $part . '/';
|
||||||
|
|
||||||
|
if (!file_exists($dirpath)) {
|
||||||
|
if (!mkdir($dirpath, $dir_perm)) {
|
||||||
|
Logger::warning('Directory could not be created', ['directory' => $dirpath]);
|
||||||
|
}
|
||||||
|
} elseif ((($old_perm = fileperms($dirpath) & 0777) != $dir_perm) && !chmod($dirpath, $dir_perm)) {
|
||||||
|
Logger::notice('Directory permissions could not be changed', ['directory' => $dirpath, 'old' => $old_perm, 'new' => $dir_perm]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((($old_group = filegroup($dirpath)) != $group) && !chgrp($dirpath, $group)) {
|
||||||
|
Logger::notice('Directory group could not be changed', ['directory' => $dirpath, 'old' => $old_group, 'new' => $group]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_put_contents($filepath, $image->asString())) {
|
||||||
|
Logger::warning('File could not be created', ['file' => $filepath]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$old_perm = fileperms($filepath) & 0666;
|
||||||
|
$old_group = filegroup($filepath);
|
||||||
|
|
||||||
|
if (($old_perm != $file_perm) && !chmod($filepath, $file_perm)) {
|
||||||
|
Logger::notice('File permissions could not be changed', ['file' => $filepath, 'old' => $old_perm, 'new' => $file_perm]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($old_group != $group) && !chgrp($filepath, $group)) {
|
||||||
|
Logger::notice('File group could not be changed', ['file' => $filepath, 'old' => $old_group, 'new' => $group]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DI::profiler()->stopRecording();
|
||||||
|
|
||||||
|
if (!file_exists($filepath)) {
|
||||||
|
Logger::warning('Avatar cache file could not be stored', ['file' => $filepath]);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return DI::baseUrl() . $path . '?ts=' . $timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the avatar cache file is locally stored
|
||||||
|
*
|
||||||
|
* @param string $avatar
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private static function isCacheFile(string $avatar): bool
|
||||||
|
{
|
||||||
|
return !empty(self::getCacheFile($avatar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the name of locally cached avatar pictures
|
||||||
|
*
|
||||||
|
* @param string $avatar
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getCacheFile(string $avatar): string
|
||||||
|
{
|
||||||
|
$parts = parse_url($avatar);
|
||||||
|
if (empty($parts['host']) || ($parts['host'] != DI::baseUrl()->getHostname())) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$pos = strpos($parts['path'], DI::baseUrl()->getUrlPath() . self::BASE_PATH);
|
||||||
|
if ($pos !== 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = DI::basePath() . $parts['path'];
|
||||||
|
|
||||||
|
DI::profiler()->startRecording('file');
|
||||||
|
$exists = file_exists($filename);
|
||||||
|
DI::profiler()->stopRecording();
|
||||||
|
|
||||||
|
if (!$exists) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete locally cached avatar pictures of a contact
|
||||||
|
*
|
||||||
|
* @param string $avatar
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function deleteCache(array $contact)
|
||||||
|
{
|
||||||
|
self::deleteCacheFile($contact['photo']);
|
||||||
|
self::deleteCacheFile($contact['thumb']);
|
||||||
|
self::deleteCacheFile($contact['micro']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a locally cached avatar picture
|
||||||
|
*
|
||||||
|
* @param string $avatar
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function deleteCacheFile(string $avatar)
|
||||||
|
{
|
||||||
|
$localFile = self::getCacheFile($avatar);
|
||||||
|
if (!empty($localFile)) {
|
||||||
|
unlink($localFile);
|
||||||
|
Logger::debug('Unlink avatar', ['avatar' => $avatar]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -184,7 +184,7 @@ class ContactSelector
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function networkToIcon($network, $profile = "")
|
public static function networkToIcon($network, $profile = "", $gsid = 0)
|
||||||
{
|
{
|
||||||
$nets = [
|
$nets = [
|
||||||
Protocol::DFRN => 'friendica',
|
Protocol::DFRN => 'friendica',
|
||||||
|
@ -218,7 +218,14 @@ class ContactSelector
|
||||||
$network_icon = str_replace($search, $replace, $network);
|
$network_icon = str_replace($search, $replace, $network);
|
||||||
|
|
||||||
if ((in_array($network, Protocol::FEDERATED)) && ($profile != "")) {
|
if ((in_array($network, Protocol::FEDERATED)) && ($profile != "")) {
|
||||||
|
if (!empty($gsid) && !empty(self::$serverdata[$gsid])) {
|
||||||
|
$gserver = self::$serverdata[$gsid];
|
||||||
|
} elseif (!empty($gsid)) {
|
||||||
|
$gserver = DBA::selectFirst('gserver', ['platform', 'network'], ['id' => $gsid]);
|
||||||
|
self::$serverdata[$gsid] = $gserver;
|
||||||
|
} else {
|
||||||
$gserver = self::getServerForProfile($profile);
|
$gserver = self::getServerForProfile($profile);
|
||||||
|
}
|
||||||
if (!empty($gserver['platform'])) {
|
if (!empty($gserver['platform'])) {
|
||||||
$network_icon = $platform_icons[strtolower($gserver['platform'])] ?? $network_icon;
|
$network_icon = $platform_icons[strtolower($gserver['platform'])] ?? $network_icon;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ use Friendica\Protocol\Activity;
|
||||||
use Friendica\Util\Crypto;
|
use Friendica\Util\Crypto;
|
||||||
use Friendica\Util\DateTimeFormat;
|
use Friendica\Util\DateTimeFormat;
|
||||||
use Friendica\Util\Profiler;
|
use Friendica\Util\Profiler;
|
||||||
use Friendica\Util\Proxy;
|
|
||||||
use Friendica\Util\Strings;
|
use Friendica\Util\Strings;
|
||||||
use Friendica\Util\Temporal;
|
use Friendica\Util\Temporal;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
@ -108,6 +107,8 @@ class Conversation
|
||||||
*/
|
*/
|
||||||
public function builtinActivityPuller(array $activity, array &$conv_responses)
|
public function builtinActivityPuller(array $activity, array &$conv_responses)
|
||||||
{
|
{
|
||||||
|
$thread_parent = $activity['thr-parent-row'] ?? [];
|
||||||
|
|
||||||
foreach ($conv_responses as $mode => $v) {
|
foreach ($conv_responses as $mode => $v) {
|
||||||
$sparkle = '';
|
$sparkle = '';
|
||||||
|
|
||||||
|
@ -152,9 +153,8 @@ class Conversation
|
||||||
$activity['thr-parent-id'] = $activity['parent-uri-id'];
|
$activity['thr-parent-id'] = $activity['parent-uri-id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip when the causer of the parent is the same than the author of the announce
|
// Skip when the causer of the parent is the same as the author of the announce
|
||||||
if (($verb == Activity::ANNOUNCE) && Post::exists(['uri-id' => $activity['thr-parent-id'],
|
if (($verb == Activity::ANNOUNCE) && !empty($thread_parent['causer-id'] && ($thread_parent['causer-id'] == $activity['author-id']))) {
|
||||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]])) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,7 +441,7 @@ class Conversation
|
||||||
$previewing = (($preview) ? ' preview ' : '');
|
$previewing = (($preview) ? ' preview ' : '');
|
||||||
|
|
||||||
if ($mode === 'network') {
|
if ($mode === 'network') {
|
||||||
$items = $this->addChildren($items, false, $order, $uid);
|
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
/*
|
/*
|
||||||
* The special div is needed for liveUpdate to kick in for this page.
|
* The special div is needed for liveUpdate to kick in for this page.
|
||||||
|
@ -467,7 +467,7 @@ class Conversation
|
||||||
. "'; </script>\r\n";
|
. "'; </script>\r\n";
|
||||||
}
|
}
|
||||||
} elseif ($mode === 'profile') {
|
} elseif ($mode === 'profile') {
|
||||||
$items = $this->addChildren($items, false, $order, $uid);
|
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
$tab = !empty($_GET['tab']) ? trim($_GET['tab']) : 'posts';
|
$tab = !empty($_GET['tab']) ? trim($_GET['tab']) : 'posts';
|
||||||
|
@ -484,7 +484,7 @@ class Conversation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($mode === 'notes') {
|
} elseif ($mode === 'notes') {
|
||||||
$items = $this->addChildren($items, false, $order, local_user());
|
$items = $this->addChildren($items, false, $order, local_user(), $mode);
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
$live_update_div = '<div id="live-notes"></div>' . "\r\n"
|
$live_update_div = '<div id="live-notes"></div>' . "\r\n"
|
||||||
|
@ -492,7 +492,7 @@ class Conversation
|
||||||
. "; var netargs = '/?f='; </script>\r\n";
|
. "; var netargs = '/?f='; </script>\r\n";
|
||||||
}
|
}
|
||||||
} elseif ($mode === 'display') {
|
} elseif ($mode === 'display') {
|
||||||
$items = $this->addChildren($items, false, $order, $uid);
|
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
$live_update_div = '<div id="live-display"></div>' . "\r\n"
|
$live_update_div = '<div id="live-display"></div>' . "\r\n"
|
||||||
|
@ -500,7 +500,7 @@ class Conversation
|
||||||
. "</script>";
|
. "</script>";
|
||||||
}
|
}
|
||||||
} elseif ($mode === 'community') {
|
} elseif ($mode === 'community') {
|
||||||
$items = $this->addChildren($items, true, $order, $uid);
|
$items = $this->addChildren($items, true, $order, $uid, $mode);
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
$live_update_div = '<div id="live-community"></div>' . "\r\n"
|
$live_update_div = '<div id="live-community"></div>' . "\r\n"
|
||||||
|
@ -510,7 +510,7 @@ class Conversation
|
||||||
. "'; </script>\r\n";
|
. "'; </script>\r\n";
|
||||||
}
|
}
|
||||||
} elseif ($mode === 'contacts') {
|
} elseif ($mode === 'contacts') {
|
||||||
$items = $this->addChildren($items, false, $order, $uid);
|
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
$live_update_div = '<div id="live-contact"></div>' . "\r\n"
|
$live_update_div = '<div id="live-contact"></div>' . "\r\n"
|
||||||
|
@ -667,15 +667,15 @@ class Conversation
|
||||||
'created_date' => $item['created'],
|
'created_date' => $item['created'],
|
||||||
'uriid' => $item['uri-id'],
|
'uriid' => $item['uri-id'],
|
||||||
'network' => $item['network'],
|
'network' => $item['network'],
|
||||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network']),
|
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network'], $item['author-gsid']),
|
||||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
|
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link'], $item['author-gsid']),
|
||||||
'linktitle' => $this->l10n->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
'linktitle' => $this->l10n->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
||||||
'profile_url' => $profile_link,
|
'profile_url' => $profile_link,
|
||||||
'item_photo_menu_html' => $this->item->photoMenu($item, $formSecurityToken),
|
'item_photo_menu_html' => $this->item->photoMenu($item, $formSecurityToken),
|
||||||
'name' => $profile_name,
|
'name' => $profile_name,
|
||||||
'sparkle' => $sparkle,
|
'sparkle' => $sparkle,
|
||||||
'lock' => false,
|
'lock' => false,
|
||||||
'thumb' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['author-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
'thumb' => $this->baseURL->remove($this->item->getAuthorAvatar($item)),
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'body_html' => $body_html,
|
'body_html' => $body_html,
|
||||||
'tags' => $tags['tags'],
|
'tags' => $tags['tags'],
|
||||||
|
@ -696,7 +696,7 @@ class Conversation
|
||||||
'indent' => '',
|
'indent' => '',
|
||||||
'owner_name' => '',
|
'owner_name' => '',
|
||||||
'owner_url' => '',
|
'owner_url' => '',
|
||||||
'owner_photo' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
'owner_photo' => $this->baseURL->remove($this->item->getOwnerAvatar($item)),
|
||||||
'plink' => ItemModel::getPlink($item),
|
'plink' => ItemModel::getPlink($item),
|
||||||
'edpost' => false,
|
'edpost' => false,
|
||||||
'pinned' => $pinned,
|
'pinned' => $pinned,
|
||||||
|
@ -811,10 +811,11 @@ class Conversation
|
||||||
*
|
*
|
||||||
* @param array $row Post row
|
* @param array $row Post row
|
||||||
* @param array $activity Contact data of the resharer
|
* @param array $activity Contact data of the resharer
|
||||||
|
* @param array $thr_parent Thread parent row
|
||||||
*
|
*
|
||||||
* @return array items with parents and comments
|
* @return array items with parents and comments
|
||||||
*/
|
*/
|
||||||
private function addRowInformation(array $row, array $activity)
|
private function addRowInformation(array $row, array $activity, array $thr_parent)
|
||||||
{
|
{
|
||||||
$this->profiler->startRecording('rendering');
|
$this->profiler->startRecording('rendering');
|
||||||
|
|
||||||
|
@ -889,6 +890,8 @@ class Conversation
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$row['thr-parent-row'] = $thr_parent;
|
||||||
|
|
||||||
$this->profiler->stopRecording();
|
$this->profiler->stopRecording();
|
||||||
return $row;
|
return $row;
|
||||||
}
|
}
|
||||||
|
@ -900,14 +903,14 @@ class Conversation
|
||||||
* This behaviour is currently needed to allow commenting on Friendica posts.
|
* This behaviour is currently needed to allow commenting on Friendica posts.
|
||||||
*
|
*
|
||||||
* @param array $parents Parent items
|
* @param array $parents Parent items
|
||||||
*
|
* @param bool $block_authors
|
||||||
* @param $block_authors
|
* @param bool $order
|
||||||
* @param $order
|
* @param int $uid
|
||||||
* @param $uid
|
* @param string $mode
|
||||||
* @return array items with parents and comments
|
* @return array items with parents and comments
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
private function addChildren(array $parents, $block_authors, $order, $uid)
|
private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode)
|
||||||
{
|
{
|
||||||
$this->profiler->startRecording('rendering');
|
$this->profiler->startRecording('rendering');
|
||||||
if (count($parents) > 1) {
|
if (count($parents) > 1) {
|
||||||
|
@ -916,8 +919,6 @@ class Conversation
|
||||||
$max_comments = $this->config->get('system', 'max_display_comments', 1000);
|
$max_comments = $this->config->get('system', 'max_display_comments', 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
$params = ['order' => ['uri-id' => true, 'uid' => true]];
|
|
||||||
|
|
||||||
$activities = [];
|
$activities = [];
|
||||||
$uriids = [];
|
$uriids = [];
|
||||||
$commentcounter = [];
|
$commentcounter = [];
|
||||||
|
@ -951,6 +952,17 @@ class Conversation
|
||||||
$condition = DBA::mergeConditions($condition,
|
$condition = DBA::mergeConditions($condition,
|
||||||
["`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW)]);
|
["`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW)]);
|
||||||
|
|
||||||
|
$thread_parents = Post::select(['uri-id', 'causer-id'], $condition, ['order' => ['uri-id' => false, 'uid']]);
|
||||||
|
|
||||||
|
$thr_parent = [];
|
||||||
|
|
||||||
|
while ($row = Post::fetch($thread_parents)) {
|
||||||
|
$thr_parent[$row['uri-id']] = $row;
|
||||||
|
}
|
||||||
|
DBA::close($thread_parents);
|
||||||
|
|
||||||
|
$params = ['order' => ['uri-id' => true, 'uid' => true]];
|
||||||
|
|
||||||
$thread_items = Post::selectForUser($uid, array_merge(ItemModel::DISPLAY_FIELDLIST, ['featured', 'contact-uid', 'gravity', 'post-type', 'post-reason']), $condition, $params);
|
$thread_items = Post::selectForUser($uid, array_merge(ItemModel::DISPLAY_FIELDLIST, ['featured', 'contact-uid', 'gravity', 'post-type', 'post-reason']), $condition, $params);
|
||||||
|
|
||||||
$items = [];
|
$items = [];
|
||||||
|
@ -960,6 +972,10 @@ class Conversation
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (($mode != 'contacts') && !$row['origin']) {
|
||||||
|
$row['featured'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ($max_comments > 0) {
|
if ($max_comments > 0) {
|
||||||
if (($row['gravity'] == GRAVITY_COMMENT) && (++$commentcounter[$row['parent-uri-id']] > $max_comments)) {
|
if (($row['gravity'] == GRAVITY_COMMENT) && (++$commentcounter[$row['parent-uri-id']] > $max_comments)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -968,7 +984,8 @@ class Conversation
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$items[$row['uri-id']] = $this->addRowInformation($row, $activities[$row['uri-id']] ?? []);
|
|
||||||
|
$items[$row['uri-id']] = $this->addRowInformation($row, $activities[$row['uri-id']] ?? [], $thr_parent[$row['thr-parent-id']] ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBA::close($thread_items);
|
DBA::close($thread_items);
|
||||||
|
|
|
@ -26,16 +26,16 @@ use Friendica\Core\Hook;
|
||||||
use Friendica\Core\L10n;
|
use Friendica\Core\L10n;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Protocol;
|
use Friendica\Core\Protocol;
|
||||||
use Friendica\Core\Session;
|
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Model\Group;
|
use Friendica\Model\Group;
|
||||||
use Friendica\Model\Item as ModelItem;
|
use Friendica\Model\Item as ModelItem;
|
||||||
|
use Friendica\Model\Photo;
|
||||||
use Friendica\Model\Tag;
|
use Friendica\Model\Tag;
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
use Friendica\Protocol\Activity;
|
use Friendica\Protocol\Activity;
|
||||||
use Friendica\Util\Profiler;
|
use Friendica\Util\Profiler;
|
||||||
use Friendica\Util\Strings;
|
use Friendica\Util\Proxy;
|
||||||
use Friendica\Util\XML;
|
use Friendica\Util\XML;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,6 +93,10 @@ class Item
|
||||||
|
|
||||||
$uid = $item['uid'] ?: $uid;
|
$uid = $item['uid'] ?: $uid;
|
||||||
|
|
||||||
|
if (empty($item['has-categories'])) {
|
||||||
|
return [$categories, $folders];
|
||||||
|
}
|
||||||
|
|
||||||
foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::CATEGORY) as $savedFolderName) {
|
foreach (Post\Category::getArrayByURIId($item['uri-id'], $uid, Post\Category::CATEGORY) as $savedFolderName) {
|
||||||
if (!empty($item['author-link'])) {
|
if (!empty($item['author-link'])) {
|
||||||
$url = $item['author-link'] . "?category=" . rawurlencode($savedFolderName);
|
$url = $item['author-link'] . "?category=" . rawurlencode($savedFolderName);
|
||||||
|
@ -353,22 +357,6 @@ class Item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches = null;
|
|
||||||
if (preg_match_all('/@\[url=(.*?)\]/is', $item['body'], $matches, PREG_SET_ORDER)) {
|
|
||||||
foreach ($matches as $mtch) {
|
|
||||||
if (!strpos($mtch[1], 'zrl=')) {
|
|
||||||
$item['body'] = str_replace($mtch[0], '@[url=' . Contact::magicLink($mtch[1]) . ']', $item['body']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add sparkle links to appropriate permalinks
|
|
||||||
// Only create a redirection to a magic link when logged in
|
|
||||||
if (!empty($item['plink']) && Session::isAuthenticated() && $item['private'] == ModelItem::PRIVATE) {
|
|
||||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
|
||||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
|
||||||
$item['plink'] = Contact::magicLinkByContact($author, $item['plink']);
|
|
||||||
}
|
|
||||||
$this->profiler->stopRecording();
|
$this->profiler->stopRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +386,7 @@ class Item
|
||||||
$pcid = $item['author-id'];
|
$pcid = $item['author-id'];
|
||||||
$network = '';
|
$network = '';
|
||||||
$rel = 0;
|
$rel = 0;
|
||||||
$condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])];
|
$condition = ['uid' => local_user(), 'uri-id' => $item['author-uri-id']];
|
||||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition);
|
$contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition);
|
||||||
if (DBA::isResult($contact)) {
|
if (DBA::isResult($contact)) {
|
||||||
$cid = $contact['id'];
|
$cid = $contact['id'];
|
||||||
|
@ -577,4 +565,43 @@ class Item
|
||||||
}
|
}
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAuthorAvatar(array $item): string
|
||||||
|
{
|
||||||
|
if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||||
|
$author_avatar = $item['contact-id'];
|
||||||
|
$author_updated = '';
|
||||||
|
$author_thumb = $item['contact-avatar'];
|
||||||
|
} else {
|
||||||
|
$author_avatar = $item['author-id'];
|
||||||
|
$author_updated = $item['author-updated'];
|
||||||
|
$author_thumb = $item['author-avatar'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (empty($author_thumb) || Photo::isPhotoURI($author_thumb)) {
|
||||||
|
$author_thumb = Contact::getAvatarUrlForId($author_avatar, Proxy::SIZE_THUMB, $author_updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $author_thumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOwnerAvatar(array $item): string
|
||||||
|
{
|
||||||
|
if (in_array($item['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||||
|
$owner_avatar = $item['contact-id'];
|
||||||
|
$owner_updated = '';
|
||||||
|
$owner_thumb = $item['contact-avatar'];
|
||||||
|
} else {
|
||||||
|
$owner_avatar = $item['owner-id'];
|
||||||
|
$owner_updated = $item['owner-updated'];
|
||||||
|
$owner_thumb = $item['owner-avatar'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($owner_thumb) || Photo::isPhotoURI($owner_thumb)) {
|
||||||
|
$owner_thumb = Contact::getAvatarUrlForId($owner_avatar, Proxy::SIZE_THUMB, $owner_updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $owner_thumb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1952,6 +1952,7 @@ class BBCode
|
||||||
* - #[url=<anything>]<term>[/url]
|
* - #[url=<anything>]<term>[/url]
|
||||||
* - [url=<anything>]#<term>[/url]
|
* - [url=<anything>]#<term>[/url]
|
||||||
*/
|
*/
|
||||||
|
self::performWithEscapedTags($text, ['url', 'share'], function ($text) use ($simple_html) {
|
||||||
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) {
|
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) {
|
||||||
if ($simple_html == self::ACTIVITYPUB) {
|
if ($simple_html == self::ACTIVITYPUB) {
|
||||||
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||||
|
@ -1963,6 +1964,8 @@ class BBCode
|
||||||
. XML::escape($matches[1]) . '</a>';
|
. XML::escape($matches[1]) . '</a>';
|
||||||
}
|
}
|
||||||
}, $text);
|
}, $text);
|
||||||
|
return $text;
|
||||||
|
});
|
||||||
|
|
||||||
// We need no target="_blank" rel="noopener noreferrer" for local links
|
// We need no target="_blank" rel="noopener noreferrer" for local links
|
||||||
// convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
|
// convert links start with DI::baseUrl() as local link without the target="_blank" rel="noopener noreferrer" attribute
|
||||||
|
@ -2086,11 +2089,15 @@ class BBCode
|
||||||
* @param string $text The text with BBCode
|
* @param string $text The text with BBCode
|
||||||
* @return string The same text - but without "abstract" element
|
* @return string The same text - but without "abstract" element
|
||||||
*/
|
*/
|
||||||
public static function stripAbstract($text)
|
public static function stripAbstract(string $text): string
|
||||||
{
|
{
|
||||||
DI::profiler()->startRecording('rendering');
|
DI::profiler()->startRecording('rendering');
|
||||||
|
|
||||||
|
$text = BBCode::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) {
|
||||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
|
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
|
||||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
|
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
|
||||||
|
return $text;
|
||||||
|
});
|
||||||
|
|
||||||
DI::profiler()->stopRecording();
|
DI::profiler()->stopRecording();
|
||||||
return $text;
|
return $text;
|
||||||
|
@ -2103,26 +2110,22 @@ class BBCode
|
||||||
* @param string $addon The addon for which the abstract is meant for
|
* @param string $addon The addon for which the abstract is meant for
|
||||||
* @return string The abstract
|
* @return string The abstract
|
||||||
*/
|
*/
|
||||||
public static function getAbstract($text, $addon = '')
|
public static function getAbstract(string $text, string $addon = ''): string
|
||||||
{
|
{
|
||||||
DI::profiler()->startRecording('rendering');
|
DI::profiler()->startRecording('rendering');
|
||||||
$abstract = '';
|
|
||||||
$abstracts = [];
|
|
||||||
$addon = strtolower($addon);
|
$addon = strtolower($addon);
|
||||||
|
|
||||||
if (preg_match_all("/\[abstract=(.*?)\](.*?)\[\/abstract\]/ism", $text, $results, PREG_SET_ORDER)) {
|
$abstract = BBCode::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) use ($addon) {
|
||||||
foreach ($results as $result) {
|
if ($addon && preg_match('#\[abstract=' . preg_quote($addon, '#') . '](.*?)\[/abstract]#ism', $text, $matches)) {
|
||||||
$abstracts[strtolower($result[1])] = $result[2];
|
return $matches[1];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($abstracts[$addon])) {
|
if (preg_match("#\[abstract](.*?)\[/abstract]#ism", $text, $matches)) {
|
||||||
$abstract = $abstracts[$addon];
|
return $matches[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($abstract == '' && preg_match("/\[abstract\](.*?)\[\/abstract\]/ism", $text, $result)) {
|
return '';
|
||||||
$abstract = $result[1];
|
});
|
||||||
}
|
|
||||||
|
|
||||||
DI::profiler()->stopRecording();
|
DI::profiler()->stopRecording();
|
||||||
return $abstract;
|
return $abstract;
|
||||||
|
@ -2337,11 +2340,9 @@ class BBCode
|
||||||
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
|
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
|
||||||
* @param callable $callback
|
* @param callable $callback
|
||||||
* @return string
|
* @return string
|
||||||
* @throws Exception
|
|
||||||
* @see Strings::performWithEscapedBlocks
|
* @see Strings::performWithEscapedBlocks
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public static function performWithEscapedTags(string $text, array $tagList, callable $callback)
|
public static function performWithEscapedTags(string $text, array $tagList, callable $callback): string
|
||||||
{
|
{
|
||||||
$tagList = array_map('preg_quote', $tagList);
|
$tagList = array_map('preg_quote', $tagList);
|
||||||
|
|
||||||
|
|
|
@ -59,11 +59,13 @@ Commands:
|
||||||
autoinstall Starts automatic installation of friendica based on values from htconfig.php
|
autoinstall Starts automatic installation of friendica based on values from htconfig.php
|
||||||
lock Edit site locks
|
lock Edit site locks
|
||||||
maintenance Set maintenance mode for this node
|
maintenance Set maintenance mode for this node
|
||||||
|
movetoavatarcache Move cached avatars to the file based avatar cache
|
||||||
user User management
|
user User management
|
||||||
php2po Generate a messages.po file from a strings.php file
|
php2po Generate a messages.po file from a strings.php file
|
||||||
po2php Generate a strings.php file from a messages.po file
|
po2php Generate a strings.php file from a messages.po file
|
||||||
typo Checks for parse errors in Friendica files
|
typo Checks for parse errors in Friendica files
|
||||||
postupdate Execute pending post update scripts (can last days)
|
postupdate Execute pending post update scripts (can last days)
|
||||||
|
relocate Update node base URL
|
||||||
serverblock Manage blocked servers
|
serverblock Manage blocked servers
|
||||||
storage Manage storage backend
|
storage Manage storage backend
|
||||||
relay Manage ActivityPub relay servers
|
relay Manage ActivityPub relay servers
|
||||||
|
@ -91,10 +93,12 @@ HELP;
|
||||||
'globalcommunitysilence' => Friendica\Console\GlobalCommunitySilence::class,
|
'globalcommunitysilence' => Friendica\Console\GlobalCommunitySilence::class,
|
||||||
'lock' => Friendica\Console\Lock::class,
|
'lock' => Friendica\Console\Lock::class,
|
||||||
'maintenance' => Friendica\Console\Maintenance::class,
|
'maintenance' => Friendica\Console\Maintenance::class,
|
||||||
|
'movetoavatarcache' => Friendica\Console\MoveToAvatarCache::class,
|
||||||
'php2po' => Friendica\Console\PhpToPo::class,
|
'php2po' => Friendica\Console\PhpToPo::class,
|
||||||
'postupdate' => Friendica\Console\PostUpdate::class,
|
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||||
'po2php' => Friendica\Console\PoToPhp::class,
|
'po2php' => Friendica\Console\PoToPhp::class,
|
||||||
'relay' => Friendica\Console\Relay::class,
|
'relay' => Friendica\Console\Relay::class,
|
||||||
|
'relocate' => Friendica\Console\Relocate::class,
|
||||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||||
'storage' => Friendica\Console\Storage::class,
|
'storage' => Friendica\Console\Storage::class,
|
||||||
'test' => Friendica\Console\Test::class,
|
'test' => Friendica\Console\Test::class,
|
||||||
|
|
|
@ -40,6 +40,7 @@ class L10n
|
||||||
'bg' => 'Български',
|
'bg' => 'Български',
|
||||||
'ca' => 'Català',
|
'ca' => 'Català',
|
||||||
'cs' => 'Česky',
|
'cs' => 'Česky',
|
||||||
|
'da-dk' => 'Dansk (Danmark)',
|
||||||
'de' => 'Deutsch',
|
'de' => 'Deutsch',
|
||||||
'en-gb' => 'English (United Kingdom)',
|
'en-gb' => 'English (United Kingdom)',
|
||||||
'en-us' => 'English (United States)',
|
'en-us' => 'English (United States)',
|
||||||
|
|
|
@ -295,7 +295,7 @@ class System
|
||||||
DI::apiResponse()->addContent(XML::fromArray(["result" => $result], $xml));
|
DI::apiResponse()->addContent(XML::fromArray(["result" => $result], $xml));
|
||||||
DI::page()->exit(DI::apiResponse()->generate());
|
DI::page()->exit(DI::apiResponse()->generate());
|
||||||
|
|
||||||
exit();
|
self::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,7 +315,7 @@ class System
|
||||||
DI::apiResponse()->addContent($content);
|
DI::apiResponse()->addContent($content);
|
||||||
DI::page()->exit(DI::apiResponse()->generate());
|
DI::page()->exit(DI::apiResponse()->generate());
|
||||||
|
|
||||||
exit();
|
self::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -331,7 +331,8 @@ class System
|
||||||
DI::apiResponse()->setType($responce, $content_type);
|
DI::apiResponse()->setType($responce, $content_type);
|
||||||
DI::apiResponse()->addContent($content);
|
DI::apiResponse()->addContent($content);
|
||||||
DI::page()->exit(DI::apiResponse()->generate());
|
DI::page()->exit(DI::apiResponse()->generate());
|
||||||
exit();
|
|
||||||
|
self::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function jsonError($httpCode, $content, $content_type = 'application/json')
|
public static function jsonError($httpCode, $content, $content_type = 'application/json')
|
||||||
|
@ -359,6 +360,16 @@ class System
|
||||||
DI::apiResponse()->setType(Response::TYPE_JSON, $content_type);
|
DI::apiResponse()->setType(Response::TYPE_JSON, $content_type);
|
||||||
DI::apiResponse()->addContent(json_encode($content, $options));
|
DI::apiResponse()->addContent(json_encode($content, $options));
|
||||||
DI::page()->exit(DI::apiResponse()->generate());
|
DI::page()->exit(DI::apiResponse()->generate());
|
||||||
|
|
||||||
|
self::exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exit the program execution.
|
||||||
|
*/
|
||||||
|
public static function exit()
|
||||||
|
{
|
||||||
|
DI::page()->logRuntime(DI::config());
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,8 +459,7 @@ class System
|
||||||
case 307:
|
case 307:
|
||||||
throw new TemporaryRedirectException();
|
throw new TemporaryRedirectException();
|
||||||
}
|
}
|
||||||
|
self::exit();
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -522,7 +532,7 @@ class System
|
||||||
echo str_replace("\t", " ", $o);
|
echo str_replace("\t", " ", $o);
|
||||||
echo "</section>";
|
echo "</section>";
|
||||||
echo "</body></html>\r\n";
|
echo "</body></html>\r\n";
|
||||||
exit();
|
self::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
|
|
||||||
namespace Friendica\Core;
|
namespace Friendica\Core;
|
||||||
|
|
||||||
use Friendica\App\Mode;
|
|
||||||
use Friendica\Core;
|
|
||||||
use Friendica\Core\Worker\Entity\Process;
|
use Friendica\Core\Worker\Entity\Process;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
@ -51,7 +49,6 @@ class Worker
|
||||||
private static $lock_duration = 0;
|
private static $lock_duration = 0;
|
||||||
private static $last_update;
|
private static $last_update;
|
||||||
private static $state;
|
private static $state;
|
||||||
private static $daemon_mode = null;
|
|
||||||
/** @var Process */
|
/** @var Process */
|
||||||
private static $process;
|
private static $process;
|
||||||
|
|
||||||
|
@ -80,7 +77,7 @@ class Worker
|
||||||
$last_cleanup = DI::config()->get('system', 'worker_last_cleaned', 0);
|
$last_cleanup = DI::config()->get('system', 'worker_last_cleaned', 0);
|
||||||
if (time() > ($last_cleanup + 300)) {
|
if (time() > ($last_cleanup + 300)) {
|
||||||
DI::config()->set('system', 'worker_last_cleaned', time());
|
DI::config()->set('system', 'worker_last_cleaned', time());
|
||||||
self::killStaleWorkers();
|
Worker\Cron::killStaleWorkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the system is ready
|
// Check if the system is ready
|
||||||
|
@ -90,7 +87,7 @@ class Worker
|
||||||
|
|
||||||
// Now we start additional cron processes if we should do so
|
// Now we start additional cron processes if we should do so
|
||||||
if ($run_cron) {
|
if ($run_cron) {
|
||||||
self::runCron();
|
Worker\Cron::run();
|
||||||
}
|
}
|
||||||
|
|
||||||
$last_check = $starttime = time();
|
$last_check = $starttime = time();
|
||||||
|
@ -98,16 +95,13 @@ class Worker
|
||||||
|
|
||||||
// We fetch the next queue entry that is about to be executed
|
// We fetch the next queue entry that is about to be executed
|
||||||
while ($r = self::workerProcess()) {
|
while ($r = self::workerProcess()) {
|
||||||
if (self::IPCJobsExists(getmypid())) {
|
if (Worker\IPC::JobsExists(getmypid())) {
|
||||||
self::IPCDeleteJobState(getmypid());
|
Worker\IPC::DeleteJobState(getmypid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't refetch when a worker fetches tasks for multiple workers
|
// Don't refetch when a worker fetches tasks for multiple workers
|
||||||
$refetched = DI::config()->get('system', 'worker_multiple_fetch');
|
$refetched = DI::config()->get('system', 'worker_multiple_fetch');
|
||||||
foreach ($r as $entry) {
|
foreach ($r as $entry) {
|
||||||
// Assure that the priority is an integer value
|
|
||||||
$entry['priority'] = (int)$entry['priority'];
|
|
||||||
|
|
||||||
// The work will be done
|
// The work will be done
|
||||||
if (!self::execute($entry)) {
|
if (!self::execute($entry)) {
|
||||||
Logger::notice('Process execution failed, quitting.');
|
Logger::notice('Process execution failed, quitting.');
|
||||||
|
@ -152,8 +146,8 @@ class Worker
|
||||||
if (time() > ($starttime + (DI::config()->get('system', 'cron_interval') * 60))) {
|
if (time() > ($starttime + (DI::config()->get('system', 'cron_interval') * 60))) {
|
||||||
Logger::info('Process lifetime reached, respawning.');
|
Logger::info('Process lifetime reached, respawning.');
|
||||||
self::unclaimProcess($process);
|
self::unclaimProcess($process);
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
self::IPCSetJobState(true);
|
Worker\IPC::SetJobState(true);
|
||||||
} else {
|
} else {
|
||||||
self::spawnWorker();
|
self::spawnWorker();
|
||||||
}
|
}
|
||||||
|
@ -162,8 +156,8 @@ class Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleaning up. Possibly not needed, but it doesn't harm anything.
|
// Cleaning up. Possibly not needed, but it doesn't harm anything.
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
self::IPCSetJobState(false);
|
Worker\IPC::SetJobState(false);
|
||||||
}
|
}
|
||||||
Logger::info("Couldn't select a workerqueue entry, quitting process", ['pid' => getmypid()]);
|
Logger::info("Couldn't select a workerqueue entry, quitting process", ['pid' => getmypid()]);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +255,7 @@ class Worker
|
||||||
$workerqueue = DBA::selectFirst('workerqueue', ['priority'], $condition, ['order' => ['priority']]);
|
$workerqueue = DBA::selectFirst('workerqueue', ['priority'], $condition, ['order' => ['priority']]);
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
self::$db_duration += (microtime(true) - $stamp);
|
||||||
if (DBA::isResult($workerqueue)) {
|
if (DBA::isResult($workerqueue)) {
|
||||||
return $workerqueue["priority"];
|
return $workerqueue['priority'];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -466,13 +460,13 @@ class Worker
|
||||||
|
|
||||||
$cooldown = DI::config()->get("system", "worker_cooldown", 0);
|
$cooldown = DI::config()->get("system", "worker_cooldown", 0);
|
||||||
if ($cooldown > 0) {
|
if ($cooldown > 0) {
|
||||||
Logger::info('Pre execution cooldown.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'cooldown' => $cooldown]);
|
Logger::info('Pre execution cooldown.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'cooldown' => $cooldown]);
|
||||||
sleep($cooldown);
|
sleep($cooldown);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::enableWorker($funcname);
|
Logger::enableWorker($funcname);
|
||||||
|
|
||||||
Logger::info("Process start.", ['priority' => $queue["priority"], 'id' => $queue["id"]]);
|
Logger::info("Process start.", ['priority' => $queue['priority'], 'id' => $queue["id"]]);
|
||||||
|
|
||||||
$stamp = (float)microtime(true);
|
$stamp = (float)microtime(true);
|
||||||
|
|
||||||
|
@ -480,11 +474,6 @@ class Worker
|
||||||
// For this reason the variables have to be initialized.
|
// For this reason the variables have to be initialized.
|
||||||
DI::profiler()->reset();
|
DI::profiler()->reset();
|
||||||
|
|
||||||
if (!in_array($queue['priority'], PRIORITIES)) {
|
|
||||||
Logger::warning('Invalid priority', ['queue' => $queue, 'callstack' => System::callstack(20)]);
|
|
||||||
$queue['priority'] = PRIORITY_MEDIUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
$a->setQueue($queue);
|
$a->setQueue($queue);
|
||||||
|
|
||||||
$up_duration = microtime(true) - self::$up_start;
|
$up_duration = microtime(true) - self::$up_start;
|
||||||
|
@ -529,21 +518,21 @@ class Worker
|
||||||
self::$lock_duration = 0;
|
self::$lock_duration = 0;
|
||||||
|
|
||||||
if ($duration > 3600) {
|
if ($duration > 3600) {
|
||||||
Logger::info('Longer than 1 hour.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
Logger::info('Longer than 1 hour.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
||||||
} elseif ($duration > 600) {
|
} elseif ($duration > 600) {
|
||||||
Logger::info('Longer than 10 minutes.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
Logger::info('Longer than 10 minutes.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
||||||
} elseif ($duration > 300) {
|
} elseif ($duration > 300) {
|
||||||
Logger::info('Longer than 5 minutes.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
Logger::info('Longer than 5 minutes.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
||||||
} elseif ($duration > 120) {
|
} elseif ($duration > 120) {
|
||||||
Logger::info('Longer than 2 minutes.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
Logger::info('Longer than 2 minutes.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'duration' => round($duration/60, 3)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::info('Process done.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'duration' => round($duration, 3)]);
|
Logger::info('Process done.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'duration' => round($duration, 3)]);
|
||||||
|
|
||||||
DI::profiler()->saveLog(DI::logger(), "ID " . $queue["id"] . ": " . $funcname);
|
DI::profiler()->saveLog(DI::logger(), "ID " . $queue["id"] . ": " . $funcname);
|
||||||
|
|
||||||
if ($cooldown > 0) {
|
if ($cooldown > 0) {
|
||||||
Logger::info('Post execution cooldown.', ['priority' => $queue["priority"], 'id' => $queue["id"], 'cooldown' => $cooldown]);
|
Logger::info('Post execution cooldown.', ['priority' => $queue['priority'], 'id' => $queue["id"], 'cooldown' => $cooldown]);
|
||||||
sleep($cooldown);
|
sleep($cooldown);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -631,87 +620,6 @@ class Worker
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* fix the queue entry if the worker process died
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
private static function killStaleWorkers()
|
|
||||||
{
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
$entries = DBA::select(
|
|
||||||
'workerqueue',
|
|
||||||
['id', 'pid', 'executed', 'priority', 'command', 'parameter'],
|
|
||||||
['NOT `done` AND `pid` != 0'],
|
|
||||||
['order' => ['priority', 'retrial', 'created']]
|
|
||||||
);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
|
|
||||||
while ($entry = DBA::fetch($entries)) {
|
|
||||||
if (!posix_kill($entry["pid"], 0)) {
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
DBA::update(
|
|
||||||
'workerqueue',
|
|
||||||
['executed' => DBA::NULL_DATETIME, 'pid' => 0],
|
|
||||||
['id' => $entry["id"]]
|
|
||||||
);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
self::$db_duration_write += (microtime(true) - $stamp);
|
|
||||||
} else {
|
|
||||||
// Kill long running processes
|
|
||||||
// Check if the priority is in a valid range
|
|
||||||
if (!in_array($entry["priority"], [PRIORITY_CRITICAL, PRIORITY_HIGH, PRIORITY_MEDIUM, PRIORITY_LOW, PRIORITY_NEGLIGIBLE])) {
|
|
||||||
$entry["priority"] = PRIORITY_MEDIUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the maximum durations
|
|
||||||
$max_duration_defaults = [PRIORITY_CRITICAL => 720, PRIORITY_HIGH => 10, PRIORITY_MEDIUM => 60, PRIORITY_LOW => 180, PRIORITY_NEGLIGIBLE => 720];
|
|
||||||
$max_duration = $max_duration_defaults[$entry["priority"]];
|
|
||||||
|
|
||||||
$argv = json_decode($entry['parameter'], true);
|
|
||||||
if (!empty($entry['command'])) {
|
|
||||||
$command = $entry['command'];
|
|
||||||
} elseif (!empty($argv)) {
|
|
||||||
$command = array_shift($argv);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$command = basename($command);
|
|
||||||
|
|
||||||
// How long is the process already running?
|
|
||||||
$duration = (time() - strtotime($entry["executed"])) / 60;
|
|
||||||
if ($duration > $max_duration) {
|
|
||||||
Logger::notice('Worker process took too much time - killed', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]);
|
|
||||||
posix_kill($entry["pid"], SIGTERM);
|
|
||||||
|
|
||||||
// We killed the stale process.
|
|
||||||
// To avoid a blocking situation we reschedule the process at the beginning of the queue.
|
|
||||||
// Additionally we are lowering the priority. (But not PRIORITY_CRITICAL)
|
|
||||||
$new_priority = $entry["priority"];
|
|
||||||
if ($entry["priority"] == PRIORITY_HIGH) {
|
|
||||||
$new_priority = PRIORITY_MEDIUM;
|
|
||||||
} elseif ($entry["priority"] == PRIORITY_MEDIUM) {
|
|
||||||
$new_priority = PRIORITY_LOW;
|
|
||||||
} elseif ($entry["priority"] != PRIORITY_CRITICAL) {
|
|
||||||
$new_priority = PRIORITY_NEGLIGIBLE;
|
|
||||||
}
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
DBA::update(
|
|
||||||
'workerqueue',
|
|
||||||
['executed' => DBA::NULL_DATETIME, 'created' => DateTimeFormat::utcNow(), 'priority' => $new_priority, 'pid' => 0],
|
|
||||||
['id' => $entry["id"]]
|
|
||||||
);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
self::$db_duration_write += (microtime(true) - $stamp);
|
|
||||||
} else {
|
|
||||||
Logger::info('Process runtime is okay', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DBA::close($entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the number of active workers exceeds the given limits
|
* Checks if the number of active workers exceeds the given limits
|
||||||
|
@ -778,12 +686,12 @@ class Worker
|
||||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||||
while ($entry = DBA::fetch($jobs)) {
|
while ($entry = DBA::fetch($jobs)) {
|
||||||
$stamp = (float)microtime(true);
|
$stamp = (float)microtime(true);
|
||||||
$running = DBA::count('workerqueue-view', ['priority' => $entry["priority"]]);
|
$running = DBA::count('workerqueue-view', ['priority' => $entry['priority']]);
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
self::$db_duration += (microtime(true) - $stamp);
|
||||||
self::$db_duration_stat += (microtime(true) - $stamp);
|
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||||
$idle_workers -= $running;
|
$idle_workers -= $running;
|
||||||
$waiting_processes += $entry["entries"];
|
$waiting_processes += $entry["entries"];
|
||||||
$listitem[$entry["priority"]] = $entry["priority"] . ":" . $running . "/" . $entry["entries"];
|
$listitem[$entry['priority']] = $entry['priority'] . ":" . $running . "/" . $entry["entries"];
|
||||||
}
|
}
|
||||||
DBA::close($jobs);
|
DBA::close($jobs);
|
||||||
} else {
|
} else {
|
||||||
|
@ -795,7 +703,7 @@ class Worker
|
||||||
|
|
||||||
while ($entry = DBA::fetch($jobs)) {
|
while ($entry = DBA::fetch($jobs)) {
|
||||||
$idle_workers -= $entry["running"];
|
$idle_workers -= $entry["running"];
|
||||||
$listitem[$entry["priority"]] = $entry["priority"].":".$entry["running"];
|
$listitem[$entry['priority']] = $entry['priority'].":".$entry["running"];
|
||||||
}
|
}
|
||||||
DBA::close($jobs);
|
DBA::close($jobs);
|
||||||
}
|
}
|
||||||
|
@ -821,8 +729,8 @@ class Worker
|
||||||
// Are there fewer workers running as possible? Then fork a new one.
|
// Are there fewer workers running as possible? Then fork a new one.
|
||||||
if (!DI::config()->get("system", "worker_dont_fork", false) && ($queues > ($active + 1)) && self::entriesExists()) {
|
if (!DI::config()->get("system", "worker_dont_fork", false) && ($queues > ($active + 1)) && self::entriesExists()) {
|
||||||
Logger::info("There are fewer workers as possible, fork a new worker.", ['active' => $active, 'queues' => $queues]);
|
Logger::info("There are fewer workers as possible, fork a new worker.", ['active' => $active, 'queues' => $queues]);
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
self::IPCSetJobState(true);
|
Worker\IPC::SetJobState(true);
|
||||||
} else {
|
} else {
|
||||||
self::spawnWorker();
|
self::spawnWorker();
|
||||||
}
|
}
|
||||||
|
@ -830,8 +738,8 @@ class Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are too much worker, we don't spawn a new one.
|
// if there are too much worker, we don't spawn a new one.
|
||||||
if (self::isDaemonMode() && ($active > $queues)) {
|
if (Worker\Daemon::isMode() && ($active > $queues)) {
|
||||||
self::IPCSetJobState(false);
|
Worker\IPC::SetJobState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $active > $queues;
|
return $active > $queues;
|
||||||
|
@ -1122,26 +1030,6 @@ class Worker
|
||||||
self::$db_duration_write += (microtime(true) - $stamp);
|
self::$db_duration_write += (microtime(true) - $stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the cron processes
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
|
||||||
*/
|
|
||||||
private static function runCron()
|
|
||||||
{
|
|
||||||
Logger::info('Add cron entries');
|
|
||||||
|
|
||||||
// Check for spooled items
|
|
||||||
self::add(['priority' => PRIORITY_HIGH, 'force_priority' => true], 'SpoolPost');
|
|
||||||
|
|
||||||
// Run the cron job that calls all other jobs
|
|
||||||
self::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], 'Cron');
|
|
||||||
|
|
||||||
// Cleaning dead processes
|
|
||||||
self::killStaleWorkers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fork a child process
|
* Fork a child process
|
||||||
*
|
*
|
||||||
|
@ -1167,11 +1055,11 @@ class Worker
|
||||||
// The parent process continues here
|
// The parent process continues here
|
||||||
DBA::connect();
|
DBA::connect();
|
||||||
|
|
||||||
self::IPCSetJobState(true, $pid);
|
Worker\IPC::SetJobState(true, $pid);
|
||||||
Logger::info('Spawned new worker', ['pid' => $pid]);
|
Logger::info('Spawned new worker', ['pid' => $pid]);
|
||||||
|
|
||||||
$cycles = 0;
|
$cycles = 0;
|
||||||
while (self::IPCJobsExists($pid) && (++$cycles < 100)) {
|
while (Worker\IPC::JobsExists($pid) && (++$cycles < 100)) {
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1186,7 +1074,7 @@ class Worker
|
||||||
$process = DI::process()->create(getmypid(), basename(__FILE__));
|
$process = DI::process()->create(getmypid(), basename(__FILE__));
|
||||||
|
|
||||||
$cycles = 0;
|
$cycles = 0;
|
||||||
while (!self::IPCJobsExists($process->pid) && (++$cycles < 100)) {
|
while (!Worker\IPC::JobsExists($process->pid) && (++$cycles < 100)) {
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1196,7 +1084,7 @@ class Worker
|
||||||
|
|
||||||
self::unclaimProcess($process);
|
self::unclaimProcess($process);
|
||||||
|
|
||||||
self::IPCSetJobState(false, $process->pid);
|
Worker\IPC::SetJobState(false, $process->pid);
|
||||||
DI::process()->delete($process);
|
DI::process()->delete($process);
|
||||||
Logger::info('Worker ended', ['pid' => $process->pid]);
|
Logger::info('Worker ended', ['pid' => $process->pid]);
|
||||||
exit();
|
exit();
|
||||||
|
@ -1211,13 +1099,13 @@ class Worker
|
||||||
*/
|
*/
|
||||||
public static function spawnWorker($do_cron = false)
|
public static function spawnWorker($do_cron = false)
|
||||||
{
|
{
|
||||||
if (self::isDaemonMode() && DI::config()->get('system', 'worker_fork')) {
|
if (Worker\Daemon::isMode() && DI::config()->get('system', 'worker_fork')) {
|
||||||
self::forkProcess($do_cron);
|
self::forkProcess($do_cron);
|
||||||
} else {
|
} else {
|
||||||
DI::system()->run('bin/worker.php', ['no_cron' => !$do_cron]);
|
DI::system()->run('bin/worker.php', ['no_cron' => !$do_cron]);
|
||||||
}
|
}
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
self::IPCSetJobState(false);
|
Worker\IPC::SetJobState(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,7 +1175,7 @@ class Worker
|
||||||
$found = DBA::exists('workerqueue', ['command' => $command, 'parameter' => $parameters, 'done' => false]);
|
$found = DBA::exists('workerqueue', ['command' => $command, 'parameter' => $parameters, 'done' => false]);
|
||||||
$added = 0;
|
$added = 0;
|
||||||
|
|
||||||
if (!in_array($priority, PRIORITIES)) {
|
if (!is_int($priority) || !in_array($priority, PRIORITIES)) {
|
||||||
Logger::warning('Invalid priority', ['priority' => $priority, 'command' => $command, 'callstack' => System::callstack(20)]);
|
Logger::warning('Invalid priority', ['priority' => $priority, 'command' => $command, 'callstack' => System::callstack(20)]);
|
||||||
$priority = PRIORITY_MEDIUM;
|
$priority = PRIORITY_MEDIUM;
|
||||||
}
|
}
|
||||||
|
@ -1308,11 +1196,11 @@ class Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the IPC flag to ensure an immediate process execution via daemon
|
// Set the IPC flag to ensure an immediate process execution via daemon
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
self::IPCSetJobState(true);
|
Worker\IPC::SetJobState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::checkDaemonState();
|
Worker\Daemon::checkState();
|
||||||
|
|
||||||
// Should we quit and wait for the worker to be called as a cronjob?
|
// Should we quit and wait for the worker to be called as a cronjob?
|
||||||
if ($dont_fork) {
|
if ($dont_fork) {
|
||||||
|
@ -1333,7 +1221,7 @@ class Worker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit on daemon mode
|
// Quit on daemon mode
|
||||||
if (self::isDaemonMode()) {
|
if (Worker\Daemon::isMode()) {
|
||||||
return $added;
|
return $added;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1423,159 +1311,6 @@ class Worker
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the flag if some job is waiting
|
|
||||||
*
|
|
||||||
* @param boolean $jobs Is there a waiting job?
|
|
||||||
* @param int $key Key number
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public static function IPCSetJobState(bool $jobs, int $key = 0)
|
|
||||||
{
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
DBA::replace('worker-ipc', ['jobs' => $jobs, 'key' => $key]);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
self::$db_duration_write += (microtime(true) - $stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a key entry
|
|
||||||
*
|
|
||||||
* @param int $key Key number
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public static function IPCDeleteJobState(int $key)
|
|
||||||
{
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
DBA::delete('worker-ipc', ['key' => $key]);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
self::$db_duration_write += (microtime(true) - $stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if some worker job waits to be executed
|
|
||||||
*
|
|
||||||
* @param int $key Key number
|
|
||||||
* @return bool
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public static function IPCJobsExists(int $key = 0)
|
|
||||||
{
|
|
||||||
$stamp = (float)microtime(true);
|
|
||||||
$row = DBA::selectFirst('worker-ipc', ['jobs'], ['key' => $key]);
|
|
||||||
self::$db_duration += (microtime(true) - $stamp);
|
|
||||||
|
|
||||||
// When we don't have a row, no job is running
|
|
||||||
if (!DBA::isResult($row)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (bool)$row['jobs'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the worker is running in the daemon mode.
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function isDaemonMode()
|
|
||||||
{
|
|
||||||
if (!is_null(self::$daemon_mode)) {
|
|
||||||
return self::$daemon_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DI::mode()->getExecutor() == Mode::DAEMON) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$daemon_mode = DI::config()->get('system', 'worker_daemon_mode', false, true);
|
|
||||||
if ($daemon_mode) {
|
|
||||||
return $daemon_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_exists('pcntl_fork')) {
|
|
||||||
self::$daemon_mode = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pidfile = DI::config()->get('system', 'pidfile');
|
|
||||||
if (empty($pidfile)) {
|
|
||||||
// No pid file, no daemon
|
|
||||||
self::$daemon_mode = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_readable($pidfile)) {
|
|
||||||
// No pid file. We assume that the daemon had been intentionally stopped.
|
|
||||||
self::$daemon_mode = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pid = intval(file_get_contents($pidfile));
|
|
||||||
$running = posix_kill($pid, 0);
|
|
||||||
|
|
||||||
self::$daemon_mode = $running;
|
|
||||||
return $running;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if the daemon is running. If not, it will be started
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private static function checkDaemonState()
|
|
||||||
{
|
|
||||||
if (!DI::config()->get('system', 'daemon_watchdog', false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DI::mode()->isNormal()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check every minute if the daemon is running
|
|
||||||
if (DI::config()->get('system', 'last_daemon_check', 0) + 60 > time()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DI::config()->set('system', 'last_daemon_check', time());
|
|
||||||
|
|
||||||
$pidfile = DI::config()->get('system', 'pidfile');
|
|
||||||
if (empty($pidfile)) {
|
|
||||||
// No pid file, no daemon
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_readable($pidfile)) {
|
|
||||||
// No pid file. We assume that the daemon had been intentionally stopped.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pid = intval(file_get_contents($pidfile));
|
|
||||||
if (posix_kill($pid, 0)) {
|
|
||||||
Logger::info('Daemon process is running', ['pid' => $pid]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::warning('Daemon process is not running', ['pid' => $pid]);
|
|
||||||
|
|
||||||
self::spawnDaemon();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spawn a new daemon process
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private static function spawnDaemon()
|
|
||||||
{
|
|
||||||
Logger::notice('Starting new daemon process');
|
|
||||||
$command = 'bin/daemon.php';
|
|
||||||
$a = DI::app();
|
|
||||||
DI::system()->run($command, ['start']);
|
|
||||||
Logger::notice('New daemon process started');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the system is inside the defined maintenance window
|
* Check if the system is inside the defined maintenance window
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Core\Worker;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Core\Worker;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Protocol\ActivityPub;
|
||||||
|
use Friendica\Util\DateTimeFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the class for jobs that are executed in an interval
|
||||||
|
*/
|
||||||
|
class Cron
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Runs the cron processes
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function run()
|
||||||
|
{
|
||||||
|
Logger::info('Add cron entries');
|
||||||
|
|
||||||
|
// Check for spooled items
|
||||||
|
Worker::add(['priority' => PRIORITY_HIGH, 'force_priority' => true], 'SpoolPost');
|
||||||
|
|
||||||
|
// Run the cron job that calls all other jobs
|
||||||
|
Worker::add(['priority' => PRIORITY_MEDIUM, 'force_priority' => true], 'Cron');
|
||||||
|
|
||||||
|
// Cleaning dead processes
|
||||||
|
self::killStaleWorkers();
|
||||||
|
|
||||||
|
// Remove old entries from the workerqueue
|
||||||
|
self::cleanWorkerQueue();
|
||||||
|
|
||||||
|
// Directly deliver or requeue posts
|
||||||
|
self::deliverPosts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fix the queue entry if the worker process died
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function killStaleWorkers()
|
||||||
|
{
|
||||||
|
$entries = DBA::select(
|
||||||
|
'workerqueue',
|
||||||
|
['id', 'pid', 'executed', 'priority', 'command', 'parameter'],
|
||||||
|
['NOT `done` AND `pid` != 0'],
|
||||||
|
['order' => ['priority', 'retrial', 'created']]
|
||||||
|
);
|
||||||
|
|
||||||
|
while ($entry = DBA::fetch($entries)) {
|
||||||
|
if (!posix_kill($entry["pid"], 0)) {
|
||||||
|
DBA::update('workerqueue', ['executed' => DBA::NULL_DATETIME, 'pid' => 0], ['id' => $entry["id"]]);
|
||||||
|
} else {
|
||||||
|
// Kill long running processes
|
||||||
|
|
||||||
|
// Define the maximum durations
|
||||||
|
$max_duration_defaults = [PRIORITY_CRITICAL => 720, PRIORITY_HIGH => 10, PRIORITY_MEDIUM => 60, PRIORITY_LOW => 180, PRIORITY_NEGLIGIBLE => 720];
|
||||||
|
$max_duration = $max_duration_defaults[$entry['priority']];
|
||||||
|
|
||||||
|
$argv = json_decode($entry['parameter'], true);
|
||||||
|
if (!empty($entry['command'])) {
|
||||||
|
$command = $entry['command'];
|
||||||
|
} elseif (!empty($argv)) {
|
||||||
|
$command = array_shift($argv);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$command = basename($command);
|
||||||
|
|
||||||
|
// How long is the process already running?
|
||||||
|
$duration = (time() - strtotime($entry["executed"])) / 60;
|
||||||
|
if ($duration > $max_duration) {
|
||||||
|
Logger::notice('Worker process took too much time - killed', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]);
|
||||||
|
posix_kill($entry["pid"], SIGTERM);
|
||||||
|
|
||||||
|
// We killed the stale process.
|
||||||
|
// To avoid a blocking situation we reschedule the process at the beginning of the queue.
|
||||||
|
// Additionally we are lowering the priority. (But not PRIORITY_CRITICAL)
|
||||||
|
$new_priority = $entry['priority'];
|
||||||
|
if ($entry['priority'] == PRIORITY_HIGH) {
|
||||||
|
$new_priority = PRIORITY_MEDIUM;
|
||||||
|
} elseif ($entry['priority'] == PRIORITY_MEDIUM) {
|
||||||
|
$new_priority = PRIORITY_LOW;
|
||||||
|
} elseif ($entry['priority'] != PRIORITY_CRITICAL) {
|
||||||
|
$new_priority = PRIORITY_NEGLIGIBLE;
|
||||||
|
}
|
||||||
|
DBA::update('workerqueue', ['executed' => DBA::NULL_DATETIME, 'created' => DateTimeFormat::utcNow(), 'priority' => $new_priority, 'pid' => 0], ['id' => $entry["id"]]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Logger::info('Process runtime is okay', ['duration' => number_format($duration, 3), 'max' => $max_duration, 'id' => $entry["id"], 'pid' => $entry["pid"], 'command' => $command]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBA::close($entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove old entries from the workerqueue
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function cleanWorkerQueue()
|
||||||
|
{
|
||||||
|
DBA::delete('workerqueue', ["`done` AND `executed` < ?", DateTimeFormat::utc('now - 1 hour')]);
|
||||||
|
|
||||||
|
// Optimizing this table only last seconds
|
||||||
|
if (DI::config()->get('system', 'optimize_tables')) {
|
||||||
|
// 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`");
|
||||||
|
DI::lock()->release(Worker::LOCK_WORKER);
|
||||||
|
}
|
||||||
|
DI::lock()->release(Worker::LOCK_PROCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly deliver AP messages or requeue them.
|
||||||
|
*
|
||||||
|
* This function is placed here as a safeguard. Even when the worker queue is completely blocked, messages will be delivered.
|
||||||
|
*/
|
||||||
|
private static function deliverPosts()
|
||||||
|
{
|
||||||
|
$deliveries = DBA::p("SELECT `item-uri`.`uri` AS `inbox`, MAX(`failed`) AS `failed` FROM `post-delivery` INNER JOIN `item-uri` ON `item-uri`.`id` = `post-delivery`.`inbox-id` GROUP BY `inbox`");
|
||||||
|
while ($delivery = DBA::fetch($deliveries)) {
|
||||||
|
if ($delivery['failed'] == 0) {
|
||||||
|
$result = ActivityPub\Delivery::deliver($delivery['inbox']);
|
||||||
|
Logger::info('Directly deliver inbox', ['inbox' => $delivery['inbox'], 'result' => $result['success']]);
|
||||||
|
continue;
|
||||||
|
} elseif ($delivery['failed'] < 3) {
|
||||||
|
$priority = PRIORITY_HIGH;
|
||||||
|
} elseif ($delivery['failed'] < 6) {
|
||||||
|
$priority = PRIORITY_MEDIUM;
|
||||||
|
} elseif ($delivery['failed'] < 8) {
|
||||||
|
$priority = PRIORITY_LOW;
|
||||||
|
} else {
|
||||||
|
$priority = PRIORITY_NEGLIGIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($delivery['failed'] >= DI::config()->get('system', 'worker_defer_limit')) {
|
||||||
|
Logger::info('Removing failed deliveries', ['inbox' => $delivery['inbox'], 'failed' => $delivery['failed']]);
|
||||||
|
Post\Delivery::removeFailed($delivery['inbox']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Worker::add($priority, 'APDelivery', '', 0, $delivery['inbox'], 0)) {
|
||||||
|
Logger::info('Missing APDelivery worker added for inbox', ['inbox' => $delivery['inbox'], 'failed' => $delivery['failed'], 'priority' => $priority]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Core\Worker;
|
||||||
|
|
||||||
|
use Friendica\App\Mode;
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\DI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the class for the worker background job processing
|
||||||
|
*/
|
||||||
|
class Daemon
|
||||||
|
{
|
||||||
|
private static $mode = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the worker is running in the daemon mode.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function isMode()
|
||||||
|
{
|
||||||
|
if (!is_null(self::$mode)) {
|
||||||
|
return self::$mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DI::mode()->getExecutor() == Mode::DAEMON) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$daemon_mode = DI::config()->get('system', 'worker_daemon_mode', false, true);
|
||||||
|
if ($daemon_mode) {
|
||||||
|
return $daemon_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists('pcntl_fork')) {
|
||||||
|
self::$mode = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pidfile = DI::config()->get('system', 'pidfile');
|
||||||
|
if (empty($pidfile)) {
|
||||||
|
// No pid file, no daemon
|
||||||
|
self::$mode = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_readable($pidfile)) {
|
||||||
|
// No pid file. We assume that the daemon had been intentionally stopped.
|
||||||
|
self::$mode = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pid = intval(file_get_contents($pidfile));
|
||||||
|
$running = posix_kill($pid, 0);
|
||||||
|
|
||||||
|
self::$mode = $running;
|
||||||
|
return $running;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the daemon is running. If not, it will be started
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function checkState()
|
||||||
|
{
|
||||||
|
if (!DI::config()->get('system', 'daemon_watchdog', false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DI::mode()->isNormal()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check every minute if the daemon is running
|
||||||
|
if (DI::config()->get('system', 'last_daemon_check', 0) + 60 > time()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DI::config()->set('system', 'last_daemon_check', time());
|
||||||
|
|
||||||
|
$pidfile = DI::config()->get('system', 'pidfile');
|
||||||
|
if (empty($pidfile)) {
|
||||||
|
// No pid file, no daemon
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_readable($pidfile)) {
|
||||||
|
// No pid file. We assume that the daemon had been intentionally stopped.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pid = intval(file_get_contents($pidfile));
|
||||||
|
if (posix_kill($pid, 0)) {
|
||||||
|
Logger::info('Daemon process is running', ['pid' => $pid]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::warning('Daemon process is not running', ['pid' => $pid]);
|
||||||
|
|
||||||
|
self::spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a new daemon process
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function spawn()
|
||||||
|
{
|
||||||
|
Logger::notice('Starting new daemon process');
|
||||||
|
DI::system()->run('bin/daemon.php', ['start']);
|
||||||
|
Logger::notice('New daemon process started');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Core\Worker;
|
||||||
|
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the class for the inter process communication
|
||||||
|
*/
|
||||||
|
class IPC
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the flag if some job is waiting
|
||||||
|
*
|
||||||
|
* @param boolean $jobs Is there a waiting job?
|
||||||
|
* @param int $key Key number
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function SetJobState(bool $jobs, int $key = 0)
|
||||||
|
{
|
||||||
|
$stamp = (float)microtime(true);
|
||||||
|
DBA::replace('worker-ipc', ['jobs' => $jobs, 'key' => $key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a key entry
|
||||||
|
*
|
||||||
|
* @param int $key Key number
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function DeleteJobState(int $key)
|
||||||
|
{
|
||||||
|
$stamp = (float)microtime(true);
|
||||||
|
DBA::delete('worker-ipc', ['key' => $key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if some worker job waits to be executed
|
||||||
|
*
|
||||||
|
* @param int $key Key number
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public static function JobsExists(int $key = 0)
|
||||||
|
{
|
||||||
|
$row = DBA::selectFirst('worker-ipc', ['jobs'], ['key' => $key]);
|
||||||
|
|
||||||
|
// When we don't have a row, no job is running
|
||||||
|
if (!DBA::isResult($row)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool)$row['jobs'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1153,7 +1153,7 @@ class Database
|
||||||
*
|
*
|
||||||
* @return boolean Was the command executed successfully?
|
* @return boolean Was the command executed successfully?
|
||||||
*/
|
*/
|
||||||
public function transaction()
|
public function transaction(): bool
|
||||||
{
|
{
|
||||||
if (!$this->performCommit()) {
|
if (!$this->performCommit()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1790,4 +1790,32 @@ class Database
|
||||||
{
|
{
|
||||||
array_walk($arr, [$this, 'escapeArrayCallback'], $add_quotation);
|
array_walk($arr, [$this, 'escapeArrayCallback'], $add_quotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces a string in the provided fields of the provided table
|
||||||
|
*
|
||||||
|
* @param string $table_name
|
||||||
|
* @param array $fields List of field names in the provided table
|
||||||
|
* @param string $search
|
||||||
|
* @param string $replace
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function replaceInTableFields(string $table_name, array $fields, string $search, string $replace)
|
||||||
|
{
|
||||||
|
$search = $this->escape($search);
|
||||||
|
$replace = $this->escape($replace);
|
||||||
|
|
||||||
|
$upd = [];
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$field = DBA::quoteIdentifier($field);
|
||||||
|
$upd[] = "$field = REPLACE($field, '$search', '$replace')";
|
||||||
|
}
|
||||||
|
|
||||||
|
$upds = implode(', ', $upd);
|
||||||
|
|
||||||
|
$r = $this->e(sprintf("UPDATE %s SET %s;", $table_name, $upds));
|
||||||
|
if (!$this->isResult($r)) {
|
||||||
|
throw new \RuntimeException("Failed updating `$table_name`: " . $this->errorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
namespace Friendica\Model;
|
namespace Friendica\Model;
|
||||||
|
|
||||||
use Friendica\App\BaseURL;
|
use Friendica\Contact\Avatar;
|
||||||
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
|
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
|
||||||
use Friendica\Content\Pager;
|
use Friendica\Content\Pager;
|
||||||
use Friendica\Content\Text\HTML;
|
use Friendica\Content\Text\HTML;
|
||||||
|
@ -35,6 +35,7 @@ use Friendica\Core\Worker;
|
||||||
use Friendica\Database\Database;
|
use Friendica\Database\Database;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
use Friendica\Module\NoScrape;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
use Friendica\Network\Probe;
|
use Friendica\Network\Probe;
|
||||||
use Friendica\Protocol\Activity;
|
use Friendica\Protocol\Activity;
|
||||||
|
@ -682,7 +683,7 @@ class Contact
|
||||||
*/
|
*/
|
||||||
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
||||||
{
|
{
|
||||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
$fields = ['id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||||
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
||||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
||||||
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||||
|
@ -714,6 +715,7 @@ class Contact
|
||||||
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
|
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
|
||||||
$fields['url'] = DI::baseUrl() . '/profile/' . $user['nickname'];
|
$fields['url'] = DI::baseUrl() . '/profile/' . $user['nickname'];
|
||||||
$fields['nurl'] = Strings::normaliseLink($fields['url']);
|
$fields['nurl'] = Strings::normaliseLink($fields['url']);
|
||||||
|
$fields['uri-id'] = ItemURI::getIdByURI($fields['url']);
|
||||||
$fields['addr'] = $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
|
$fields['addr'] = $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
|
||||||
$fields['request'] = DI::baseUrl() . '/dfrn_request/' . $user['nickname'];
|
$fields['request'] = DI::baseUrl() . '/dfrn_request/' . $user['nickname'];
|
||||||
$fields['notify'] = DI::baseUrl() . '/dfrn_notify/' . $user['nickname'];
|
$fields['notify'] = DI::baseUrl() . '/dfrn_notify/' . $user['nickname'];
|
||||||
|
@ -771,10 +773,10 @@ class Contact
|
||||||
$fields['updated'] = DateTimeFormat::utcNow();
|
$fields['updated'] = DateTimeFormat::utcNow();
|
||||||
self::update($fields, ['id' => $self['id']]);
|
self::update($fields, ['id' => $self['id']]);
|
||||||
|
|
||||||
// Update the public contact as well
|
// Update the other contacts as well
|
||||||
$fields['prvkey'] = null;
|
unset($fields['prvkey']);
|
||||||
$fields['self'] = false;
|
$fields['self'] = false;
|
||||||
self::update($fields, ['uid' => 0, 'nurl' => $self['nurl']]);
|
self::update($fields, ['uri-id' => $self['uri-id'], 'self' => false]);
|
||||||
|
|
||||||
// Update the profile
|
// Update the profile
|
||||||
$fields = [
|
$fields = [
|
||||||
|
@ -797,14 +799,20 @@ class Contact
|
||||||
public static function remove($id)
|
public static function remove($id)
|
||||||
{
|
{
|
||||||
// We want just to make sure that we don't delete our "self" contact
|
// We want just to make sure that we don't delete our "self" contact
|
||||||
$contact = DBA::selectFirst('contact', ['uid'], ['id' => $id, 'self' => false]);
|
$contact = DBA::selectFirst('contact', ['uri-id', 'photo', 'thumb', 'micro', 'uid'], ['id' => $id, 'self' => false]);
|
||||||
if (!DBA::isResult($contact)) {
|
if (!DBA::isResult($contact)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||||
|
|
||||||
// Archive the contact
|
// Archive the contact
|
||||||
self::update(['archive' => true, 'network' => Protocol::PHANTOM, 'deleted' => true], ['id' => $id]);
|
self::update(['archive' => true, 'network' => Protocol::PHANTOM, 'deleted' => true], ['id' => $id]);
|
||||||
|
|
||||||
|
if (!DBA::exists('contact', ['uri-id' => $contact['uri-id'], 'deleted' => false])) {
|
||||||
|
Avatar::deleteCache($contact);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete it in the background
|
// Delete it in the background
|
||||||
Worker::add(PRIORITY_MEDIUM, 'Contact\Remove', $id);
|
Worker::add(PRIORITY_MEDIUM, 'Contact\Remove', $id);
|
||||||
}
|
}
|
||||||
|
@ -827,9 +835,11 @@ class Contact
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||||
|
if (!empty($cdata['public'])) {
|
||||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self::removeSharer($contact);
|
self::removeSharer($contact);
|
||||||
}
|
}
|
||||||
|
@ -854,9 +864,11 @@ class Contact
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||||
|
if (!empty($cdata['public'])) {
|
||||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self::removeFollower($contact);
|
self::removeFollower($contact);
|
||||||
}
|
}
|
||||||
|
@ -878,19 +890,29 @@ class Contact
|
||||||
throw new \InvalidArgumentException('Unexpected public contact record');
|
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||||
}
|
}
|
||||||
|
|
||||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||||
|
|
||||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
if (in_array($contact['rel'], [self::SHARING, self::FRIEND]) && !empty($cdata['public'])) {
|
||||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND]) && !empty($cdata['public'])) {
|
||||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::remove($contact['id']);
|
self::remove($contact['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function clearFollowerFollowingEndpointCache(int $uid)
|
||||||
|
{
|
||||||
|
if (empty($uid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_CONTACTS . 'followers:' . $uid);
|
||||||
|
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_CONTACTS . 'following:' . $uid);
|
||||||
|
DI::cache()->delete(NoScrape::CACHEKEY . $uid);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a contact for archival after a communication issue delay
|
* Marks a contact for archival after a communication issue delay
|
||||||
|
@ -1460,7 +1482,7 @@ class Contact
|
||||||
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
||||||
|
|
||||||
if ($pager->getStart() == 0) {
|
if ($pager->getStart() == 0) {
|
||||||
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
|
$cdata = self::getPublicAndUserContactID($cid, local_user());
|
||||||
if (!empty($cdata['public'])) {
|
if (!empty($cdata['public'])) {
|
||||||
$pinned = Post\Collection::selectToArrayForContact($cdata['public'], Post\Collection::FEATURED, $fields);
|
$pinned = Post\Collection::selectToArrayForContact($cdata['public'], Post\Collection::FEATURED, $fields);
|
||||||
$items = array_merge($items, $pinned);
|
$items = array_merge($items, $pinned);
|
||||||
|
@ -1473,7 +1495,7 @@ class Contact
|
||||||
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
||||||
|
|
||||||
if ($pager->getStart() == 0) {
|
if ($pager->getStart() == 0) {
|
||||||
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
|
$cdata = self::getPublicAndUserContactID($cid, local_user());
|
||||||
if (!empty($cdata['public'])) {
|
if (!empty($cdata['public'])) {
|
||||||
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
||||||
$cdata['public'], Post\Collection::FEATURED];
|
$cdata['public'], Post\Collection::FEATURED];
|
||||||
|
@ -1567,14 +1589,22 @@ class Contact
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Network::isLocalLink($contact['url'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) || DI::config()->get('system', 'cache_contact_avatar')) {
|
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) || DI::config()->get('system', 'cache_contact_avatar')) {
|
||||||
if (!empty($contact['avatar']) && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) {
|
if (!empty($contact['avatar']) && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) {
|
||||||
Logger::info('Adding avatar cache', ['id' => $cid, 'contact' => $contact]);
|
Logger::info('Adding avatar cache', ['id' => $cid, 'contact' => $contact]);
|
||||||
self::updateAvatar($cid, $contact['avatar'], true);
|
self::updateAvatar($cid, $contact['avatar'], true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} elseif (!empty($contact['photo']) || !empty($contact['thumb']) || !empty($contact['micro'])) {
|
} elseif (Photo::isPhotoURI($contact['photo']) || Photo::isPhotoURI($contact['thumb']) || Photo::isPhotoURI($contact['micro'])) {
|
||||||
Logger::info('Removing avatar cache', ['id' => $cid, 'contact' => $contact]);
|
Logger::info('Replacing legacy avatar cache', ['id' => $cid, 'contact' => $contact]);
|
||||||
|
self::updateAvatar($cid, $contact['avatar'], true);
|
||||||
|
return;
|
||||||
|
} elseif (DI::config()->get('system', 'avatar_cache') && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) {
|
||||||
|
Logger::info('Adding avatar cache file', ['id' => $cid, 'contact' => $contact]);
|
||||||
self::updateAvatar($cid, $contact['avatar'], true);
|
self::updateAvatar($cid, $contact['avatar'], true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1593,6 +1623,27 @@ class Contact
|
||||||
private static function getAvatarPath(array $contact, string $size, $no_update = false)
|
private static function getAvatarPath(array $contact, string $size, $no_update = false)
|
||||||
{
|
{
|
||||||
$contact = self::checkAvatarCacheByArray($contact, $no_update);
|
$contact = self::checkAvatarCacheByArray($contact, $no_update);
|
||||||
|
|
||||||
|
if (DI::config()->get('system', 'avatar_cache')) {
|
||||||
|
switch ($size) {
|
||||||
|
case Proxy::SIZE_MICRO:
|
||||||
|
if (!empty($contact['micro']) && !Photo::isPhotoURI($contact['micro'])) {
|
||||||
|
return $contact['micro'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Proxy::SIZE_THUMB:
|
||||||
|
if (!empty($contact['thumb']) && !Photo::isPhotoURI($contact['thumb'])) {
|
||||||
|
return $contact['thumb'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Proxy::SIZE_SMALL:
|
||||||
|
if (!empty($contact['photo']) && !Photo::isPhotoURI($contact['photo'])) {
|
||||||
|
return $contact['photo'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
|
return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1657,7 +1708,9 @@ class Contact
|
||||||
return $contact;
|
return $contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($contact['id']) && !empty($contact['avatar'])) {
|
$local = !empty($contact['url']) && Network::isLocalLink($contact['url']);
|
||||||
|
|
||||||
|
if (!$local && !empty($contact['id']) && !empty($contact['avatar'])) {
|
||||||
self::updateAvatar($contact['id'], $contact['avatar'], true);
|
self::updateAvatar($contact['id'], $contact['avatar'], true);
|
||||||
|
|
||||||
$new_contact = self::getById($contact['id'], $contact_fields);
|
$new_contact = self::getById($contact['id'], $contact_fields);
|
||||||
|
@ -1665,6 +1718,8 @@ class Contact
|
||||||
// We only update the cache fields
|
// We only update the cache fields
|
||||||
$contact = array_merge($contact, $new_contact);
|
$contact = array_merge($contact, $new_contact);
|
||||||
}
|
}
|
||||||
|
} elseif ($local && !empty($contact['avatar'])) {
|
||||||
|
return $contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add the default avatars if the fields aren't filled
|
/// add the default avatars if the fields aren't filled
|
||||||
|
@ -1799,7 +1854,7 @@ class Contact
|
||||||
{
|
{
|
||||||
// We have to fetch the "updated" variable when it wasn't provided
|
// We have to fetch the "updated" variable when it wasn't provided
|
||||||
// The parameter can be provided to improve performance
|
// The parameter can be provided to improve performance
|
||||||
if (empty($updated) || empty($guid)) {
|
if (empty($updated)) {
|
||||||
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
|
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
|
||||||
$updated = $account['updated'] ?? '';
|
$updated = $account['updated'] ?? '';
|
||||||
$guid = $account['guid'] ?? '';
|
$guid = $account['guid'] ?? '';
|
||||||
|
@ -1901,7 +1956,7 @@ class Contact
|
||||||
*/
|
*/
|
||||||
public static function updateAvatar(int $cid, string $avatar, bool $force = false, bool $create_cache = false)
|
public static function updateAvatar(int $cid, string $avatar, bool $force = false, bool $create_cache = false)
|
||||||
{
|
{
|
||||||
$contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'xmpp', 'addr', 'nurl', 'url', 'network'],
|
$contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'],
|
||||||
['id' => $cid, 'self' => false]);
|
['id' => $cid, 'self' => false]);
|
||||||
if (!DBA::isResult($contact)) {
|
if (!DBA::isResult($contact)) {
|
||||||
return;
|
return;
|
||||||
|
@ -1942,6 +1997,8 @@ class Contact
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) || $cache_avatar) {
|
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) || $cache_avatar) {
|
||||||
|
Avatar::deleteCache($contact);
|
||||||
|
|
||||||
if ($default_avatar && Proxy::isLocalImage($avatar)) {
|
if ($default_avatar && Proxy::isLocalImage($avatar)) {
|
||||||
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
||||||
'photo' => $avatar,
|
'photo' => $avatar,
|
||||||
|
@ -1993,9 +2050,8 @@ class Contact
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Photo::delete(['uid' => $uid, 'contact-id' => $cid, 'photo-type' => Photo::CONTACT_AVATAR]);
|
Photo::delete(['uid' => $uid, 'contact-id' => $cid, 'photo-type' => Photo::CONTACT_AVATAR]);
|
||||||
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
$fields = Avatar::fetchAvatarContact($contact, $avatar, $force);
|
||||||
'photo' => '', 'thumb' => '', 'micro' => ''];
|
$update = ($avatar . $fields['photo'] . $fields['thumb'] . $fields['micro'] != $contact['avatar'] . $contact['photo'] . $contact['thumb'] . $contact['micro']) || $force;
|
||||||
$update = ($avatar != $contact['avatar'] . $contact['photo'] . $contact['thumb'] . $contact['micro']) || $force;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$update) {
|
if (!$update) {
|
||||||
|
@ -2274,7 +2330,10 @@ class Contact
|
||||||
|
|
||||||
if ($uid == 0) {
|
if ($uid == 0) {
|
||||||
if ($ret['network'] == Protocol::ACTIVITYPUB) {
|
if ($ret['network'] == Protocol::ACTIVITYPUB) {
|
||||||
ActivityPub\Processor::fetchFeaturedPosts($ret['url']);
|
$apcontact = APContact::getByURL($ret['url'], false);
|
||||||
|
if (!empty($apcontact['featured'])) {
|
||||||
|
Worker::add(PRIORITY_LOW, 'FetchFeaturedPosts', $ret['url']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ret['last-item'] = Probe::getLastUpdate($ret);
|
$ret['last-item'] = Probe::getLastUpdate($ret);
|
||||||
|
@ -2484,6 +2543,11 @@ class Contact
|
||||||
} else {
|
} else {
|
||||||
$probed = true;
|
$probed = true;
|
||||||
$ret = Probe::uri($url, $network, $uid);
|
$ret = Probe::uri($url, $network, $uid);
|
||||||
|
|
||||||
|
// Ensure that the public contact exists
|
||||||
|
if ($ret['network'] != Protocol::PHANTOM) {
|
||||||
|
self::getIdForURL($url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($network != '') && ($ret['network'] != $network)) {
|
if (($network != '') && ($ret['network'] != $network)) {
|
||||||
|
@ -2662,6 +2726,8 @@ class Contact
|
||||||
$contact = DBA::selectFirst('contact', [], ['id' => $cid]);
|
$contact = DBA::selectFirst('contact', [], ['id' => $cid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::clearFollowerFollowingEndpointCache($importer['uid']);
|
||||||
|
|
||||||
if (!empty($contact)) {
|
if (!empty($contact)) {
|
||||||
if (!empty($contact['pending'])) {
|
if (!empty($contact['pending'])) {
|
||||||
Logger::info('Pending contact request already exists.', ['url' => $url, 'uid' => $importer['uid']]);
|
Logger::info('Pending contact request already exists.', ['url' => $url, 'uid' => $importer['uid']]);
|
||||||
|
@ -2785,7 +2851,9 @@ class Contact
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||||
|
|
||||||
|
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||||
|
|
||||||
DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]);
|
DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]);
|
||||||
}
|
}
|
||||||
|
@ -2799,6 +2867,8 @@ class Contact
|
||||||
*/
|
*/
|
||||||
public static function removeSharer(array $contact)
|
public static function removeSharer(array $contact)
|
||||||
{
|
{
|
||||||
|
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||||
|
|
||||||
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||||
self::remove($contact['id']);
|
self::remove($contact['id']);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -42,6 +42,7 @@ use Friendica\Util\Map;
|
||||||
use Friendica\Util\Network;
|
use Friendica\Util\Network;
|
||||||
use Friendica\Util\Proxy;
|
use Friendica\Util\Proxy;
|
||||||
use Friendica\Util\Strings;
|
use Friendica\Util\Strings;
|
||||||
|
use Friendica\Util\Temporal;
|
||||||
use Friendica\Worker\Delivery;
|
use Friendica\Worker\Delivery;
|
||||||
use LanguageDetection\Language;
|
use LanguageDetection\Language;
|
||||||
|
|
||||||
|
@ -87,14 +88,16 @@ class Item
|
||||||
'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language',
|
'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language',
|
||||||
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
|
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
|
||||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global',
|
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global',
|
||||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'author-updated', 'author-gsid', 'author-addr', 'author-uri-id',
|
||||||
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type',
|
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated',
|
||||||
'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network',
|
'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network',
|
||||||
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
|
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
|
||||||
'writable', 'self', 'cid', 'alias',
|
'writable', 'self', 'cid', 'alias',
|
||||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||||
'event-nofinish', 'event-ignore', 'event-id',
|
'event-nofinish', 'event-ignore', 'event-id',
|
||||||
|
"question-id", "question-multiple", "question-voters", "question-end-time",
|
||||||
|
"has-categories", "has-media",
|
||||||
'delivery_queue_count', 'delivery_queue_done', 'delivery_queue_failed'
|
'delivery_queue_count', 'delivery_queue_done', 'delivery_queue_failed'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -725,7 +728,7 @@ class Item
|
||||||
return GRAVITY_UNKNOWN; // Should not happen
|
return GRAVITY_UNKNOWN; // Should not happen
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function insert(array $item, bool $notify = false, bool $post_local = true)
|
public static function insert(array $item, int $notify = 0, bool $post_local = true)
|
||||||
{
|
{
|
||||||
$orig_item = $item;
|
$orig_item = $item;
|
||||||
|
|
||||||
|
@ -739,7 +742,7 @@ class Item
|
||||||
$item['protocol'] = Conversation::PARCEL_DIRECT;
|
$item['protocol'] = Conversation::PARCEL_DIRECT;
|
||||||
$item['direction'] = Conversation::PUSH;
|
$item['direction'] = Conversation::PUSH;
|
||||||
|
|
||||||
if (in_array($notify, PRIORITIES)) {
|
if (is_int($notify) && in_array($notify, PRIORITIES)) {
|
||||||
$priority = $notify;
|
$priority = $notify;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1079,7 +1082,7 @@ class Item
|
||||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
|
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
|
||||||
$content_warning = BBCode::getAbstract($item['body'], Protocol::ACTIVITYPUB);
|
$content_warning = BBCode::getAbstract($item['body'], Protocol::ACTIVITYPUB);
|
||||||
if (!empty($content_warning) && empty($item['content-warning'])) {
|
if (!empty($content_warning) && empty($item['content-warning'])) {
|
||||||
$item['content-warning'] = $content_warning;
|
$item['content-warning'] = BBCode::toPlaintext($content_warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1215,9 +1218,30 @@ class Item
|
||||||
Worker::add(['priority' => $priority, 'dont_fork' => true], 'Notifier', $notify_type, (int)$posted_item['uri-id'], (int)$posted_item['uid']);
|
Worker::add(['priority' => $priority, 'dont_fork' => true], 'Notifier', $notify_type, (int)$posted_item['uri-id'], (int)$posted_item['uid']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill the cache with the rendered content.
|
||||||
|
if (in_array($posted_item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT]) && ($posted_item['uid'] == 0)) {
|
||||||
|
self::updateDisplayCache($posted_item['uri-id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($posted_item['origin'] && ($posted_item['uid'] != 0) && in_array($posted_item['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT])) {
|
||||||
|
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_OUTBOX . $posted_item['uid']);
|
||||||
|
}
|
||||||
|
|
||||||
return $post_user_id;
|
return $post_user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the display cache
|
||||||
|
*
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function updateDisplayCache(int $uri_id)
|
||||||
|
{
|
||||||
|
$item = Post::selectFirst(self::DISPLAY_FIELDLIST, ['uri-id' => $uri_id]);
|
||||||
|
self::prepareBody($item, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the owner of a parent item if it had been shared by a forum
|
* Change the owner of a parent item if it had been shared by a forum
|
||||||
*
|
*
|
||||||
|
@ -1425,7 +1449,7 @@ class Item
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
if (($uri_id != $item['thr-parent-id']) && (($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||||
// Fetch the origin user for the post
|
// Fetch the origin user for the post
|
||||||
$origin_uid = self::GetOriginUidForUriId($item['thr-parent-id'], $uid);
|
$origin_uid = self::GetOriginUidForUriId($item['thr-parent-id'], $uid);
|
||||||
if (is_null($origin_uid)) {
|
if (is_null($origin_uid)) {
|
||||||
|
@ -2692,7 +2716,7 @@ class Item
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
* @todo Remove reference, simply return "rendered-html" and "rendered-hash"
|
* @todo Remove reference, simply return "rendered-html" and "rendered-hash"
|
||||||
*/
|
*/
|
||||||
public static function putInCache(&$item)
|
private static function putInCache(&$item)
|
||||||
{
|
{
|
||||||
// Save original body to prevent addons to modify it
|
// Save original body to prevent addons to modify it
|
||||||
$body = $item['body'];
|
$body = $item['body'];
|
||||||
|
@ -2705,8 +2729,6 @@ class Item
|
||||||
|| $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body)
|
|| $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body)
|
||||||
|| DI::config()->get('system', 'ignore_cache')
|
|| DI::config()->get('system', 'ignore_cache')
|
||||||
) {
|
) {
|
||||||
self::addRedirToImageTags($item);
|
|
||||||
|
|
||||||
$item['rendered-html'] = BBCode::convertForUriId($item['uri-id'], $item['body']);
|
$item['rendered-html'] = BBCode::convertForUriId($item['uri-id'], $item['body']);
|
||||||
$item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
|
$item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
|
||||||
|
|
||||||
|
@ -2731,31 +2753,6 @@ class Item
|
||||||
$item['body'] = $body;
|
$item['body'] = $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find any non-embedded images in private items and add redir links to them
|
|
||||||
*
|
|
||||||
* @param array &$item The field array of an item row
|
|
||||||
*/
|
|
||||||
private static function addRedirToImageTags(array &$item)
|
|
||||||
{
|
|
||||||
$app = DI::app();
|
|
||||||
|
|
||||||
$matches = [];
|
|
||||||
$cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
|
|
||||||
if ($cnt) {
|
|
||||||
foreach ($matches as $mtch) {
|
|
||||||
if (strpos($mtch[1], '/redir') !== false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((local_user() == $item['uid']) && ($item['private'] == self::PRIVATE) && ($item['contact-id'] != $app->getContactId()) && ($item['network'] == Protocol::DFRN)) {
|
|
||||||
$img_url = 'redir/' . $item['contact-id'] . '?url=' . urlencode($mtch[1]);
|
|
||||||
$item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an item array, convert the body element from bbcode to html and add smilie icons.
|
* Given an item array, convert the body element from bbcode to html and add smilie icons.
|
||||||
* If attach is true, also add icons for item attachments.
|
* If attach is true, also add icons for item attachments.
|
||||||
|
@ -2763,6 +2760,7 @@ class Item
|
||||||
* @param array $item
|
* @param array $item
|
||||||
* @param boolean $attach
|
* @param boolean $attach
|
||||||
* @param boolean $is_preview
|
* @param boolean $is_preview
|
||||||
|
* @param boolean $only_cache
|
||||||
* @return string item body html
|
* @return string item body html
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
* @throws \ImagickException
|
* @throws \ImagickException
|
||||||
|
@ -2771,7 +2769,7 @@ class Item
|
||||||
* @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
|
* @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
|
||||||
* @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
|
* @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
|
||||||
*/
|
*/
|
||||||
public static function prepareBody(array &$item, $attach = false, $is_preview = false)
|
public static function prepareBody(array &$item, $attach = false, $is_preview = false, $only_cache = false)
|
||||||
{
|
{
|
||||||
$a = DI::app();
|
$a = DI::app();
|
||||||
Hook::callAll('prepare_body_init', $item);
|
Hook::callAll('prepare_body_init', $item);
|
||||||
|
@ -2792,10 +2790,10 @@ class Item
|
||||||
$body = $item['body'] ?? '';
|
$body = $item['body'] ?? '';
|
||||||
$shared = BBCode::fetchShareAttributes($body);
|
$shared = BBCode::fetchShareAttributes($body);
|
||||||
if (!empty($shared['guid'])) {
|
if (!empty($shared['guid'])) {
|
||||||
$shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
|
$shared_item = Post::selectFirst(['uri-id', 'plink', 'has-media'], ['guid' => $shared['guid']]);
|
||||||
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
||||||
$shared_links = [strtolower($shared_item['plink'] ?? '')];
|
$shared_links = [strtolower($shared_item['plink'] ?? '')];
|
||||||
$shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
|
$shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid'], [], $shared_item['has-media'] ?? false);
|
||||||
$shared_links = array_merge($shared_links, array_column($shared_attachments['visual'], 'url'));
|
$shared_links = array_merge($shared_links, array_column($shared_attachments['visual'], 'url'));
|
||||||
$shared_links = array_merge($shared_links, array_column($shared_attachments['link'], 'url'));
|
$shared_links = array_merge($shared_links, array_column($shared_attachments['link'], 'url'));
|
||||||
$shared_links = array_merge($shared_links, array_column($shared_attachments['additional'], 'url'));
|
$shared_links = array_merge($shared_links, array_column($shared_attachments['additional'], 'url'));
|
||||||
|
@ -2804,7 +2802,7 @@ class Item
|
||||||
$shared_uri_id = 0;
|
$shared_uri_id = 0;
|
||||||
$shared_links = [];
|
$shared_links = [];
|
||||||
}
|
}
|
||||||
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links);
|
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links, $item['has-media']);
|
||||||
$item['body'] = self::replaceVisualAttachments($attachments, $item['body'] ?? '');
|
$item['body'] = self::replaceVisualAttachments($attachments, $item['body'] ?? '');
|
||||||
|
|
||||||
$item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']);
|
$item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']);
|
||||||
|
@ -2812,6 +2810,10 @@ class Item
|
||||||
$item['body'] = $body;
|
$item['body'] = $body;
|
||||||
$s = $item["rendered-html"];
|
$s = $item["rendered-html"];
|
||||||
|
|
||||||
|
if ($only_cache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Compile eventual content filter reasons
|
// Compile eventual content filter reasons
|
||||||
$filter_reasons = [];
|
$filter_reasons = [];
|
||||||
if (!$is_preview && public_contact() != $item['author-id']) {
|
if (!$is_preview && public_contact() != $item['author-id']) {
|
||||||
|
@ -2857,6 +2859,7 @@ class Item
|
||||||
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
||||||
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
|
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
|
||||||
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
||||||
|
$s = self::addQuestions($item, $s);
|
||||||
|
|
||||||
// Map.
|
// Map.
|
||||||
if (strpos($s, '<div class="map">') !== false && !empty($item['coord'])) {
|
if (strpos($s, '<div class="map">') !== false && !empty($item['coord'])) {
|
||||||
|
@ -2927,9 +2930,17 @@ class Item
|
||||||
|
|
||||||
foreach ($attachments['visual'] as $attachment) {
|
foreach ($attachments['visual'] as $attachment) {
|
||||||
if (!empty($attachment['preview'])) {
|
if (!empty($attachment['preview'])) {
|
||||||
$body = str_replace($attachment['preview'], Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE), $body);
|
$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') {
|
} elseif ($attachment['filetype'] == 'image') {
|
||||||
$body = str_replace($attachment['url'], Post\Media::getUrlForId($attachment['id']), $body);
|
$proxy = Post\Media::getUrlForId($attachment['id']);
|
||||||
|
$search = ['[img=' . $attachment['url'] . ']', ']' . $attachment['url'] . '[/img]'];
|
||||||
|
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
|
||||||
|
|
||||||
|
$body = str_replace($search, $replace, $body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DI::profiler()->stopRecording();
|
DI::profiler()->stopRecording();
|
||||||
|
@ -3182,6 +3193,50 @@ class Item
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function addQuestions(array $item, string $content)
|
||||||
|
{
|
||||||
|
DI::profiler()->startRecording('rendering');
|
||||||
|
if (!empty($item['question-id'])) {
|
||||||
|
$question = [
|
||||||
|
'id' => $item['question-id'],
|
||||||
|
'multiple' => $item['question-multiple'],
|
||||||
|
'voters' => $item['question-voters'],
|
||||||
|
'endtime' => $item['question-end-time']
|
||||||
|
];
|
||||||
|
|
||||||
|
$options = Post\QuestionOption::getByURIId($item['uri-id']);
|
||||||
|
foreach ($options as $key => $option) {
|
||||||
|
$percent = $question['voters'] ? ($option['replies'] / $question['voters'] * 100) : 0;
|
||||||
|
|
||||||
|
$options[$key]['percent'] = $percent;
|
||||||
|
|
||||||
|
if ($question['voters'] > 0) {
|
||||||
|
$options[$key]['vote'] = DI::l10n()->t('%s (%d%s, %d votes)', $option['name'], round($percent, 1), '%', $option['replies']);
|
||||||
|
} else {
|
||||||
|
$options[$key]['vote'] = DI::l10n()->t('%s (%d votes)', $option['name'], $option['replies']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($question['voters']) && !empty($question['endtime'])) {
|
||||||
|
$summary = DI::l10n()->t('%d voters. Poll end: %s', $question['voters'], Temporal::getRelativeDate($question['endtime']));
|
||||||
|
} elseif (!empty($question['voters'])) {
|
||||||
|
$summary = DI::l10n()->t('%d voters.', $question['voters']);
|
||||||
|
} elseif (!empty($question['endtime'])) {
|
||||||
|
$summary = DI::l10n()->t('Poll end: %s', Temporal::getRelativeDate($question['endtime']));
|
||||||
|
} else {
|
||||||
|
$summary = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$content .= Renderer::replaceMacros(Renderer::getMarkupTemplate('content/question.tpl'), [
|
||||||
|
'$question' => $question,
|
||||||
|
'$options' => $options,
|
||||||
|
'$summary' => $summary,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
DI::profiler()->stopRecording();
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get private link for item
|
* get private link for item
|
||||||
*
|
*
|
||||||
|
@ -3205,6 +3260,12 @@ class Item
|
||||||
'orig_title' => DI::l10n()->t('View on separate page'),
|
'orig_title' => DI::l10n()->t('View on separate page'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!empty($plink) && ($item['private'] == self::PRIVATE)) {
|
||||||
|
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||||
|
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||||
|
$plink = Contact::magicLinkByContact($author, $plink);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($plink)) {
|
if (!empty($plink)) {
|
||||||
$ret['href'] = DI::baseUrl()->remove($plink);
|
$ret['href'] = DI::baseUrl()->remove($plink);
|
||||||
$ret['title'] = DI::l10n()->t('Link to source');
|
$ret['title'] = DI::l10n()->t('Link to source');
|
||||||
|
|
|
@ -704,10 +704,19 @@ class Photo
|
||||||
}
|
}
|
||||||
$image_uri = substr($image_uri, strrpos($image_uri, '/') + 1);
|
$image_uri = substr($image_uri, strrpos($image_uri, '/') + 1);
|
||||||
$image_uri = substr($image_uri, 0, strpos($image_uri, '-'));
|
$image_uri = substr($image_uri, 0, strpos($image_uri, '-'));
|
||||||
if (!strlen($image_uri)) {
|
return trim($image_uri);
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
return $image_uri;
|
|
||||||
|
/**
|
||||||
|
* Checks if the given URL is a local photo.
|
||||||
|
* Since it is meant for time critical occasions, the check is done without any database requests.
|
||||||
|
*
|
||||||
|
* @param string $url
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static function isPhotoURI(string $url): bool
|
||||||
|
{
|
||||||
|
return !empty(self::ridFromURI($url));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -111,6 +111,11 @@ class Category
|
||||||
return array_column($tags, 'name');
|
return array_column($tags, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function existsForURIId(int $uri_id, int $uid)
|
||||||
|
{
|
||||||
|
return DBA::exists('post-category', ['uri-id' => $uri_id, 'uid' => $uid]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an array of files or categories of a given uri-id
|
* Generates an array of files or categories of a given uri-id
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace Friendica\Model\Post;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use BadMethodCallException;
|
use BadMethodCallException;
|
||||||
use Friendica\Database\Database;
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Protocol\ActivityPub;
|
||||||
|
|
||||||
class Collection
|
class Collection
|
||||||
{
|
{
|
||||||
|
@ -34,14 +36,19 @@ class Collection
|
||||||
*
|
*
|
||||||
* @param integer $uri_id
|
* @param integer $uri_id
|
||||||
* @param integer $type
|
* @param integer $type
|
||||||
|
* @param integer $cache_uid If set to a non zero value, the featured cache is cleared
|
||||||
*/
|
*/
|
||||||
public static function add(int $uri_id, int $type)
|
public static function add(int $uri_id, int $type, int $cache_uid = 0)
|
||||||
{
|
{
|
||||||
if (empty($uri_id)) {
|
if (empty($uri_id)) {
|
||||||
throw new BadMethodCallException('Empty URI_id');
|
throw new BadMethodCallException('Empty URI_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
DBA::insert('post-collection', ['uri-id' => $uri_id, 'type' => $type], Database::INSERT_IGNORE);
|
DBA::insert('post-collection', ['uri-id' => $uri_id, 'type' => $type], Database::INSERT_IGNORE);
|
||||||
|
|
||||||
|
if (!empty($cache_uid) && ($type == self::FEATURED)) {
|
||||||
|
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_FEATURED . $cache_uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,14 +56,19 @@ class Collection
|
||||||
*
|
*
|
||||||
* @param integer $uri_id
|
* @param integer $uri_id
|
||||||
* @param integer $type
|
* @param integer $type
|
||||||
|
* @param integer $cache_uid If set to a non zero value, the featured cache is cleared
|
||||||
*/
|
*/
|
||||||
public static function remove(int $uri_id, int $type)
|
public static function remove(int $uri_id, int $type, int $cache_uid = 0)
|
||||||
{
|
{
|
||||||
if (empty($uri_id)) {
|
if (empty($uri_id)) {
|
||||||
throw new BadMethodCallException('Empty URI_id');
|
throw new BadMethodCallException('Empty URI_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
DBA::delete('post-collection', ['uri-id' => $uri_id, 'type' => $type]);
|
DBA::delete('post-collection', ['uri-id' => $uri_id, 'type' => $type]);
|
||||||
|
|
||||||
|
if (!empty($cache_uid) && ($type == self::FEATURED)) {
|
||||||
|
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_FEATURED . $cache_uid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Model\Post;
|
||||||
|
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use BadMethodCallException;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\ItemURI;
|
||||||
|
|
||||||
|
class Delivery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Add a post to an inbox
|
||||||
|
*
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @param string $inbox
|
||||||
|
* @param string $created
|
||||||
|
* @param array %receivers
|
||||||
|
*/
|
||||||
|
public static function add(int $uri_id, int $uid, string $inbox, string $created, string $command, array $receivers)
|
||||||
|
{
|
||||||
|
if (empty($uri_id)) {
|
||||||
|
throw new BadMethodCallException('Empty URI_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = ['uri-id' => $uri_id, 'uid' => $uid, 'inbox-id' => ItemURI::getIdByURI($inbox),
|
||||||
|
'created' => $created, 'command' => $command, 'receivers' => json_encode($receivers)];
|
||||||
|
|
||||||
|
DBA::insert('post-delivery', $fields, Database::INSERT_IGNORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove post from an inbox after delivery
|
||||||
|
*
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @param string $inbox
|
||||||
|
*/
|
||||||
|
public static function remove(int $uri_id, string $inbox)
|
||||||
|
{
|
||||||
|
DBA::delete('post-delivery', ['uri-id' => $uri_id, 'inbox-id' => ItemURI::getIdByURI($inbox)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove failed posts for an inbox
|
||||||
|
*
|
||||||
|
* @param string $inbox
|
||||||
|
*/
|
||||||
|
public static function removeFailed(string $inbox)
|
||||||
|
{
|
||||||
|
DBA::delete('post-delivery', ["`inbox-id` = ? AND `failed` >= ?", ItemURI::getIdByURI($inbox), DI::config()->get('system', 'worker_defer_limit')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment "failed" counter for the given inbox and post
|
||||||
|
*
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @param string $inbox
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function selectForInbox(string $inbox)
|
||||||
|
{
|
||||||
|
$rows = DBA::select('post-delivery', [], ["`inbox-id` = ? AND `failed` < ?", ItemURI::getIdByURI($inbox), DI::config()->get('system', 'worker_defer_limit')], ['order' => ['created']]);
|
||||||
|
$deliveries = [];
|
||||||
|
while ($row = DBA::fetch($rows)) {
|
||||||
|
if (!empty($row['receivers'])) {
|
||||||
|
$row['receivers'] = json_decode($row['receivers'], true);
|
||||||
|
} else {
|
||||||
|
$row['receivers'] = [];
|
||||||
|
}
|
||||||
|
$deliveries[] = $row;
|
||||||
|
}
|
||||||
|
DBA::close($rows);
|
||||||
|
|
||||||
|
return $deliveries;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Friendica\Model\Post;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\Database\Database;
|
||||||
|
use Friendica\Database\DBStructure;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
|
||||||
|
class History
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Add a post to the history before it is changed
|
||||||
|
*
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @param array $item
|
||||||
|
*/
|
||||||
|
public static function add(int $uri_id, array $item)
|
||||||
|
{
|
||||||
|
$allfields = DBStructure::definition('', false);
|
||||||
|
$fields = array_keys($allfields['post-history']['fields']);
|
||||||
|
|
||||||
|
$post = Post::selectFirstPost($fields, ['uri-id' => $uri_id]);
|
||||||
|
if (empty($post)) {
|
||||||
|
Logger::warning('Post not found', ['uri-id' => $uri_id]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item['edited'] <= $post['edited']) {
|
||||||
|
Logger::info('New edit date is not newer than the old one', ['uri-id' => $uri_id, 'old' => $post['edited'], 'new' => $item['edited']]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$update = false;
|
||||||
|
$changed = DBStructure::getFieldsForTable('post-history', $item);
|
||||||
|
unset($changed['uri-id']);
|
||||||
|
unset($changed['edited']);
|
||||||
|
foreach ($changed as $field => $content) {
|
||||||
|
if ($content != $post[$field]) {
|
||||||
|
$update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($update) {
|
||||||
|
DBA::insert('post-history', $post, Database::INSERT_IGNORE);
|
||||||
|
Logger::info('Added history', ['uri-id' => $uri_id, 'edited' => $post['edited']]);
|
||||||
|
} else {
|
||||||
|
Logger::info('No content fields had been changed', ['uri-id' => $uri_id, 'edited' => $post['edited']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -547,12 +547,17 @@ class Media
|
||||||
* @param int $uri_id
|
* @param int $uri_id
|
||||||
* @param string $guid
|
* @param string $guid
|
||||||
* @param array $links list of links that shouldn't be added
|
* @param array $links list of links that shouldn't be added
|
||||||
|
* @param bool $has_media
|
||||||
* @return array attachments
|
* @return array attachments
|
||||||
*/
|
*/
|
||||||
public static function splitAttachments(int $uri_id, string $guid = '', array $links = [])
|
public static function splitAttachments(int $uri_id, string $guid = '', array $links = [], bool $has_media = true)
|
||||||
{
|
{
|
||||||
$attachments = ['visual' => [], 'link' => [], 'additional' => []];
|
$attachments = ['visual' => [], 'link' => [], 'additional' => []];
|
||||||
|
|
||||||
|
if (!$has_media) {
|
||||||
|
return $attachments;
|
||||||
|
}
|
||||||
|
|
||||||
$media = self::getByURIId($uri_id);
|
$media = self::getByURIId($uri_id);
|
||||||
if (empty($media)) {
|
if (empty($media)) {
|
||||||
return $attachments;
|
return $attachments;
|
||||||
|
|
|
@ -22,11 +22,9 @@
|
||||||
namespace Friendica\Model\Post;
|
namespace Friendica\Model\Post;
|
||||||
|
|
||||||
use \BadMethodCallException;
|
use \BadMethodCallException;
|
||||||
use Friendica\Core\Protocol;
|
|
||||||
use Friendica\Database\Database;
|
use Friendica\Database\Database;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\Database\DBStructure;
|
use Friendica\Database\DBStructure;
|
||||||
use Friendica\Model\Post;
|
|
||||||
|
|
||||||
class Thread
|
class Thread
|
||||||
{
|
{
|
||||||
|
|
|
@ -308,7 +308,7 @@ class UserNotification
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForUser(
|
$notification = DI::notificationFactory()->createForUser(
|
||||||
$uid,
|
$uid,
|
||||||
$item['vid'],
|
$item['vid'],
|
||||||
$type,
|
$type,
|
||||||
|
@ -336,7 +336,7 @@ class UserNotification
|
||||||
*/
|
*/
|
||||||
public static function insertNotification(int $actor, string $verb, int $uid): bool
|
public static function insertNotification(int $actor, string $verb, int $uid): bool
|
||||||
{
|
{
|
||||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForRelationship(
|
$notification = DI::notificationFactory()->createForRelationship(
|
||||||
$uid,
|
$uid,
|
||||||
$actor,
|
$actor,
|
||||||
$verb
|
$verb
|
||||||
|
|
|
@ -220,7 +220,7 @@ class Profile
|
||||||
public static function load(App $a, string $nickname, bool $show_contacts = true)
|
public static function load(App $a, string $nickname, bool $show_contacts = true)
|
||||||
{
|
{
|
||||||
$profile = User::getOwnerDataByNick($nickname);
|
$profile = User::getOwnerDataByNick($nickname);
|
||||||
if (empty($profile)) {
|
if (empty($profile) || $profile['account_removed']) {
|
||||||
Logger::info('profile error: ' . DI::args()->getQueryString());
|
Logger::info('profile error: ' . DI::args()->getQueryString());
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -235,7 +235,7 @@ class Profile
|
||||||
|
|
||||||
DI::page()['title'] = $profile['name'] . ' @ ' . DI::config()->get('config', 'sitename');
|
DI::page()['title'] = $profile['name'] . ' @ ' . DI::config()->get('config', 'sitename');
|
||||||
|
|
||||||
if (!DI::pConfig()->get(local_user(), 'system', 'always_my_theme')) {
|
if (!local_user()) {
|
||||||
$a->setCurrentTheme($profile['theme']);
|
$a->setCurrentTheme($profile['theme']);
|
||||||
$a->setCurrentMobileTheme(DI::pConfig()->get($a->getProfileOwner(), 'system', 'mobile_theme'));
|
$a->setCurrentMobileTheme(DI::pConfig()->get($a->getProfileOwner(), 'system', 'mobile_theme'));
|
||||||
}
|
}
|
||||||
|
@ -880,23 +880,17 @@ class Profile
|
||||||
*
|
*
|
||||||
* Used from within PCSS themes to set theme parameters. If there's a
|
* Used from within PCSS themes to set theme parameters. If there's a
|
||||||
* profile_uid variable set in App, that is the "page owner" and normally their theme
|
* profile_uid variable set in App, that is the "page owner" and normally their theme
|
||||||
* settings take precedence; unless a local user sets the "always_my_theme"
|
* settings take precedence; unless a local user is logged in which means they don't
|
||||||
* system pconfig, which means they don't want to see anybody else's theme
|
* want to see anybody else's theme settings except their own while on this site.
|
||||||
* settings except their own while on this site.
|
|
||||||
*
|
*
|
||||||
|
* @param App $a
|
||||||
* @return int user ID
|
* @return int user ID
|
||||||
*
|
*
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
|
||||||
* @note Returns local_user instead of user ID if "always_my_theme" is set to true
|
* @note Returns local_user instead of user ID if "always_my_theme" is set to true
|
||||||
*/
|
*/
|
||||||
public static function getThemeUid(App $a)
|
public static function getThemeUid(App $a): int
|
||||||
{
|
{
|
||||||
$uid = !empty($a->getProfileOwner()) ? intval($a->getProfileOwner()) : 0;
|
return local_user() ?: $a->getProfileOwner();
|
||||||
if (local_user() && (DI::pConfig()->get(local_user(), 'system', 'always_my_theme') || !$uid)) {
|
|
||||||
return local_user();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $uid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -143,17 +143,7 @@ class Subscription
|
||||||
{
|
{
|
||||||
$type = \Friendica\Factory\Api\Mastodon\Notification::getType($Notification);
|
$type = \Friendica\Factory\Api\Mastodon\Notification::getType($Notification);
|
||||||
|
|
||||||
$desktop_notification = !in_array($type, [Notification::TYPE_RESHARE, Notification::TYPE_LIKE]);
|
if (DI::notify()->NotifyOnDesktop($Notification, $type)) {
|
||||||
|
|
||||||
if (DI::pConfig()->get($Notification->uid, 'system', 'notify_like') && ($type == Notification::TYPE_LIKE)) {
|
|
||||||
$desktop_notification = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DI::pConfig()->get($Notification->uid, 'system', 'notify_announce') && ($type == Notification::TYPE_RESHARE)) {
|
|
||||||
$desktop_notification = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($desktop_notification) {
|
|
||||||
DI::notify()->createFromNotification($Notification);
|
DI::notify()->createFromNotification($Notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,13 +224,16 @@ class Tag
|
||||||
{
|
{
|
||||||
$fields = ['name' => substr($name, 0, 96), 'url' => $url];
|
$fields = ['name' => substr($name, 0, 96), 'url' => $url];
|
||||||
|
|
||||||
if (!empty($type)) {
|
$tag = DBA::selectFirst('tag', ['id', 'type'], $fields);
|
||||||
$fields['type'] = $type;
|
if (DBA::isResult($tag)) {
|
||||||
|
if (empty($tag['type']) && !empty($type)) {
|
||||||
|
DBA::update('tag', ['type' => $type], $fields);
|
||||||
|
}
|
||||||
|
return $tag['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$tag = DBA::selectFirst('tag', ['id'], $fields);
|
if (!empty($type)) {
|
||||||
if (DBA::isResult($tag)) {
|
$fields['type'] = $type;
|
||||||
return $tag['id'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DBA::insert('tag', $fields, Database::INSERT_IGNORE);
|
DBA::insert('tag', $fields, Database::INSERT_IGNORE);
|
||||||
|
@ -295,16 +298,29 @@ class Tag
|
||||||
{
|
{
|
||||||
Logger::info('Check for tags', ['uri-id' => $uriid, 'hash' => $tags, 'callstack' => System::callstack()]);
|
Logger::info('Check for tags', ['uri-id' => $uriid, 'hash' => $tags, 'callstack' => System::callstack()]);
|
||||||
|
|
||||||
$result = self::getFromBody($body, $tags);
|
if (is_null($tags)) {
|
||||||
if (empty($result)) {
|
$tags = self::TAG_CHARACTER[self::HASHTAG] . self::TAG_CHARACTER[self::MENTION] . self::TAG_CHARACTER[self::EXCLUSIVE_MENTION];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::info('Found tags', ['uri-id' => $uriid, 'hash' => $tags, 'result' => $result]);
|
// Only remove the shared data from "real" reshares
|
||||||
|
$shared = BBCode::fetchShareAttributes($body);
|
||||||
|
if (!empty($shared['guid'])) {
|
||||||
|
if (preg_match("/\s*\[share .*?\](.*?)\[\/share\]\s*/ism", $body, $matches)) {
|
||||||
|
$share_body = $matches[1];
|
||||||
|
}
|
||||||
|
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($result as $tag) {
|
foreach (self::getFromBody($body, $tags) as $tag) {
|
||||||
self::storeByHash($uriid, $tag[1], $tag[3], $tag[2], $probing);
|
self::storeByHash($uriid, $tag[1], $tag[3], $tag[2], $probing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search for hashtags in the shared body (but only if hashtags are wanted)
|
||||||
|
if (!empty($share_body) && (strpos($tags, self::TAG_CHARACTER[self::HASHTAG]) !== false)) {
|
||||||
|
foreach (self::getFromBody($share_body, self::TAG_CHARACTER[self::HASHTAG]) as $tag) {
|
||||||
|
self::storeByHash($uriid, $tag[1], $tag[3], $tag[2], $probing);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,6 +43,7 @@ class Federation extends BaseAdmin
|
||||||
'diaspora' => ['name' => 'Diaspora', 'color' => '#a1a1a1'], // logo is black and white, makes a gray
|
'diaspora' => ['name' => 'Diaspora', 'color' => '#a1a1a1'], // logo is black and white, makes a gray
|
||||||
'funkwhale' => ['name' => 'Funkwhale', 'color' => '#4082B4'], // From the homepage
|
'funkwhale' => ['name' => 'Funkwhale', 'color' => '#4082B4'], // From the homepage
|
||||||
'gnusocial' => ['name' => 'GNU Social/Statusnet', 'color' => '#a22430'], // dark red from the logo
|
'gnusocial' => ['name' => 'GNU Social/Statusnet', 'color' => '#a22430'], // dark red from the logo
|
||||||
|
'gotosocial' => ['name' => 'GoToSocial', 'color' => '#df8958'], // Some color from their mascot
|
||||||
'hometown' => ['name' => 'Hometown', 'color' => '#1f70c1'], // Color from the Patreon page
|
'hometown' => ['name' => 'Hometown', 'color' => '#1f70c1'], // Color from the Patreon page
|
||||||
'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
|
'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
|
||||||
'lemmy' => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
|
'lemmy' => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
|
||||||
|
@ -154,7 +155,7 @@ class Federation extends BaseAdmin
|
||||||
$versionCounts = self::reformaDiasporaVersions($versionCounts);
|
$versionCounts = self::reformaDiasporaVersions($versionCounts);
|
||||||
} elseif ($platform == 'relay') {
|
} elseif ($platform == 'relay') {
|
||||||
$versionCounts = self::reformatRelayVersions($versionCounts);
|
$versionCounts = self::reformatRelayVersions($versionCounts);
|
||||||
} elseif (in_array($platform, ['funkwhale', 'mastodon', 'mobilizon', 'misskey'])) {
|
} elseif (in_array($platform, ['funkwhale', 'mastodon', 'mobilizon', 'misskey', 'gotosocial'])) {
|
||||||
$versionCounts = self::removeVersionSuffixes($versionCounts);
|
$versionCounts = self::removeVersionSuffixes($versionCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
namespace Friendica\Module\Admin;
|
namespace Friendica\Module\Admin;
|
||||||
|
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Module\BaseAdmin;
|
use Friendica\Module\BaseAdmin;
|
||||||
|
|
||||||
class PhpInfo extends BaseAdmin
|
class PhpInfo extends BaseAdmin
|
||||||
|
@ -30,6 +31,6 @@ class PhpInfo extends BaseAdmin
|
||||||
self::checkAdminAccess();
|
self::checkAdminAccess();
|
||||||
|
|
||||||
phpinfo();
|
phpinfo();
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module\Admin;
|
namespace Friendica\Module\Admin;
|
||||||
|
|
||||||
use Friendica\App;
|
use Friendica\App;
|
||||||
|
use Friendica\Core\Relocate;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
use Friendica\Core\Search;
|
use Friendica\Core\Search;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
|
@ -60,74 +61,6 @@ class Site extends BaseAdmin
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// relocate
|
|
||||||
// @TODO This file could benefit from moving this feature away in a Module\Admin\Relocate class for example
|
|
||||||
if (!empty($_POST['relocate']) && !empty($_POST['relocate_url']) && $_POST['relocate_url'] != "") {
|
|
||||||
$new_url = $_POST['relocate_url'];
|
|
||||||
$new_url = rtrim($new_url, "/");
|
|
||||||
|
|
||||||
$parsed = @parse_url($new_url);
|
|
||||||
if (!is_array($parsed) || empty($parsed['host']) || empty($parsed['scheme'])) {
|
|
||||||
notice(DI::l10n()->t("Can not parse base url. Must have at least <scheme>://<domain>"));
|
|
||||||
DI::baseUrl()->redirect('admin/site');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* steps:
|
|
||||||
* replace all "baseurl" to "new_url" in config, profile, term, items and contacts
|
|
||||||
* send relocate for every local user
|
|
||||||
* */
|
|
||||||
|
|
||||||
$old_url = DI::baseUrl()->get(true);
|
|
||||||
|
|
||||||
// Generate host names for relocation the addresses in the format user@address.tld
|
|
||||||
$new_host = str_replace("http://", "@", Strings::normaliseLink($new_url));
|
|
||||||
$old_host = str_replace("http://", "@", Strings::normaliseLink($old_url));
|
|
||||||
|
|
||||||
function update_table(App $a, $table_name, $fields, $old_url, $new_url)
|
|
||||||
{
|
|
||||||
$dbold = DBA::escape($old_url);
|
|
||||||
$dbnew = DBA::escape($new_url);
|
|
||||||
|
|
||||||
$upd = [];
|
|
||||||
foreach ($fields as $f) {
|
|
||||||
$upd[] = "`$f` = REPLACE(`$f`, '$dbold', '$dbnew')";
|
|
||||||
}
|
|
||||||
|
|
||||||
$upds = implode(", ", $upd);
|
|
||||||
|
|
||||||
$r = DBA::e(sprintf("UPDATE %s SET %s;", $table_name, $upds));
|
|
||||||
if (!DBA::isResult($r)) {
|
|
||||||
notice("Failed updating '$table_name': " . DBA::errorMessage());
|
|
||||||
DI::baseUrl()->redirect('admin/site');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update tables
|
|
||||||
// update profile links in the format "http://server.tld"
|
|
||||||
update_table($a, "profile", ['photo', 'thumb'], $old_url, $new_url);
|
|
||||||
update_table($a, "contact", ['photo', 'thumb', 'micro', 'url', 'nurl', 'alias', 'request', 'notify', 'poll', 'confirm', 'poco', 'avatar'], $old_url, $new_url);
|
|
||||||
update_table($a, "post-content", ['body'], $old_url, $new_url);
|
|
||||||
|
|
||||||
// update profile addresses in the format "user@server.tld"
|
|
||||||
update_table($a, "contact", ['addr'], $old_host, $new_host);
|
|
||||||
|
|
||||||
// update config
|
|
||||||
DI::config()->set('system', 'url', $new_url);
|
|
||||||
DI::baseUrl()->saveByURL($new_url);
|
|
||||||
|
|
||||||
// send relocate
|
|
||||||
$usersStmt = DBA::select('user', ['uid'], ['account_removed' => false, 'account_expired' => false]);
|
|
||||||
while ($user = DBA::fetch($usersStmt)) {
|
|
||||||
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $user['uid']);
|
|
||||||
}
|
|
||||||
DBA::close($usersStmt);
|
|
||||||
|
|
||||||
info(DI::l10n()->t("Relocation started. Could take a while to complete."));
|
|
||||||
|
|
||||||
DI::baseUrl()->redirect('admin/site');
|
|
||||||
}
|
|
||||||
// end relocate
|
|
||||||
|
|
||||||
$sitename = (!empty($_POST['sitename']) ? trim($_POST['sitename']) : '');
|
$sitename = (!empty($_POST['sitename']) ? trim($_POST['sitename']) : '');
|
||||||
$sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : '');
|
$sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : '');
|
||||||
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
|
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
|
||||||
|
@ -512,8 +445,9 @@ class Site extends BaseAdmin
|
||||||
'$no_relay_list' => DI::l10n()->t('The system is not subscribed to any relays at the moment.'),
|
'$no_relay_list' => DI::l10n()->t('The system is not subscribed to any relays at the moment.'),
|
||||||
'$relay_list_title' => DI::l10n()->t('The system is currently subscribed to the following relays:'),
|
'$relay_list_title' => DI::l10n()->t('The system is currently subscribed to the following relays:'),
|
||||||
'$relay_list' => Relay::getList(['url']),
|
'$relay_list' => Relay::getList(['url']),
|
||||||
'$relocate' => DI::l10n()->t('Relocate Instance'),
|
'$relocate' => DI::l10n()->t('Relocate Node'),
|
||||||
'$relocate_warning' => DI::l10n()->t('<strong>Warning!</strong> Advanced function. Could make this server unreachable.'),
|
'$relocate_msg' => DI::l10n()->t('Relocating your node enables you to change the DNS domain of this node and keep all the existing users and posts. This process takes a while and can only be started from the relocate console command like this:'),
|
||||||
|
'$relocate_cmd' => DI::l10n()->t('(Friendica directory)# bin/console relocate https://newdomain.com'),
|
||||||
'$baseurl' => DI::baseUrl()->get(true),
|
'$baseurl' => DI::baseUrl()->get(true),
|
||||||
|
|
||||||
// name, label, value, help string, extra data...
|
// name, label, value, help string, extra data...
|
||||||
|
@ -601,8 +535,6 @@ class Site extends BaseAdmin
|
||||||
'$temppath' => ['temppath', DI::l10n()->t('Temp path'), DI::config()->get('system', 'temppath'), DI::l10n()->t('If you have a restricted system where the webserver can\'t access the system temp path, enter another path here.')],
|
'$temppath' => ['temppath', DI::l10n()->t('Temp path'), DI::config()->get('system', 'temppath'), DI::l10n()->t('If you have a restricted system where the webserver can\'t access the system temp path, enter another path here.')],
|
||||||
'$only_tag_search' => ['only_tag_search', DI::l10n()->t('Only search in tags'), DI::config()->get('system', 'only_tag_search'), DI::l10n()->t('On large systems the text search can slow down the system extremely.')],
|
'$only_tag_search' => ['only_tag_search', DI::l10n()->t('Only search in tags'), DI::config()->get('system', 'only_tag_search'), DI::l10n()->t('On large systems the text search can slow down the system extremely.')],
|
||||||
|
|
||||||
'$relocate_url' => ['relocate_url', DI::l10n()->t('New base url'), DI::baseUrl()->get(), DI::l10n()->t('Change base url for this server. Sends relocate message to all Friendica and Diaspora* contacts of all users.')],
|
|
||||||
|
|
||||||
'$worker_queues' => ['worker_queues', DI::l10n()->t('Maximum number of parallel workers'), DI::config()->get('system', 'worker_queues'), DI::l10n()->t('On shared hosters set this to %d. On larger systems, values of %d are great. Default value is %d.', 5, 20, 10)],
|
'$worker_queues' => ['worker_queues', DI::l10n()->t('Maximum number of parallel workers'), DI::config()->get('system', 'worker_queues'), DI::l10n()->t('On shared hosters set this to %d. On larger systems, values of %d are great. Default value is %d.', 5, 20, 10)],
|
||||||
'$worker_fastlane' => ['worker_fastlane', DI::l10n()->t('Enable fastlane'), DI::config()->get('system', 'worker_fastlane'), DI::l10n()->t('When enabed, the fastlane mechanism starts an additional worker if processes with higher priority are blocked by processes of lower priority.')],
|
'$worker_fastlane' => ['worker_fastlane', DI::l10n()->t('Enable fastlane'), DI::config()->get('system', 'worker_fastlane'), DI::l10n()->t('When enabed, the fastlane mechanism starts an additional worker if processes with higher priority are blocked by processes of lower priority.')],
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Pin extends BaseApi
|
||||||
DI::mstdnError()->RecordNotFound();
|
DI::mstdnError()->RecordNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED);
|
Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED, $uid);
|
||||||
|
|
||||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Unpin extends BaseApi
|
||||||
DI::mstdnError()->RecordNotFound();
|
DI::mstdnError()->RecordNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED);
|
Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED, $uid);
|
||||||
|
|
||||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Attach as MAttach;
|
use Friendica\Model\Attach as MAttach;
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ class Attach extends BaseModule
|
||||||
}
|
}
|
||||||
|
|
||||||
echo $data;
|
echo $data;
|
||||||
exit();
|
System::exit();
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ use Friendica\Model;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
use Friendica\Object\Search\ContactResult;
|
use Friendica\Object\Search\ContactResult;
|
||||||
use Friendica\Object\Search\ResultList;
|
use Friendica\Object\Search\ResultList;
|
||||||
|
use Friendica\Util\Network;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for search modules
|
* Base class for search modules
|
||||||
|
@ -68,7 +69,7 @@ class BaseSearch extends BaseModule
|
||||||
$header = DI::l10n()->t('People Search - %s', $search);
|
$header = DI::l10n()->t('People Search - %s', $search);
|
||||||
|
|
||||||
if (strrpos($search, '@') > 0) {
|
if (strrpos($search, '@') > 0) {
|
||||||
$results = Search::getContactsFromProbe($search);
|
$results = Search::getContactsFromProbe(Network::convertToIdn($search));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,6 +79,8 @@ class BaseSearch extends BaseModule
|
||||||
$header = DI::l10n()->t('Forum Search - %s', $search);
|
$header = DI::l10n()->t('Forum Search - %s', $search);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$search = Network::convertToIdn($search);
|
||||||
|
|
||||||
if (DI::mode()->isMobile()) {
|
if (DI::mode()->isMobile()) {
|
||||||
$itemsPerPage = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
|
$itemsPerPage = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
|
||||||
DI::config()->get('system', 'itemspage_network_mobile'));
|
DI::config()->get('system', 'itemspage_network_mobile'));
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace Friendica\Module\Contact;
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
use Friendica\Core\Session;
|
use Friendica\Core\Session;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
|
@ -107,6 +108,6 @@ class Hovercard extends BaseModule
|
||||||
]);
|
]);
|
||||||
|
|
||||||
echo $o;
|
echo $o;
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module\Debug;
|
namespace Friendica\Module\Debug;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
|
@ -48,7 +49,7 @@ class ItemBody extends BaseModule
|
||||||
if (!empty($item)) {
|
if (!empty($item)) {
|
||||||
if (DI::mode()->isAjax()) {
|
if (DI::mode()->isAjax()) {
|
||||||
echo str_replace("\n", '<br />', $item['body']);
|
echo str_replace("\n", '<br />', $item['body']);
|
||||||
exit();
|
System::exit();
|
||||||
} else {
|
} else {
|
||||||
return str_replace("\n", '<br />', $item['body']);
|
return str_replace("\n", '<br />', $item['body']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ use Friendica\BaseModule;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Protocol\Feed as ProtocolFeed;
|
use Friendica\Protocol\Feed as ProtocolFeed;
|
||||||
|
use Friendica\Network\HTTPException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides public Atom feeds
|
* Provides public Atom feeds
|
||||||
|
@ -42,7 +43,7 @@ use Friendica\Protocol\Feed as ProtocolFeed;
|
||||||
*/
|
*/
|
||||||
class Feed extends BaseModule
|
class Feed extends BaseModule
|
||||||
{
|
{
|
||||||
protected function content(array $request = []): string
|
protected function rawContent(array $request = [])
|
||||||
{
|
{
|
||||||
$last_update = $request['last_update'] ?? '';
|
$last_update = $request['last_update'] ?? '';
|
||||||
$nocache = !empty($request['nocache']) && local_user();
|
$nocache = !empty($request['nocache']) && local_user();
|
||||||
|
@ -66,6 +67,11 @@ class Feed extends BaseModule
|
||||||
$type = 'posts';
|
$type = 'posts';
|
||||||
}
|
}
|
||||||
|
|
||||||
System::httpExit(ProtocolFeed::atom($this->parameters['nickname'], $last_update, 10, $type, $nocache, true), Response::TYPE_ATOM);
|
$feed = ProtocolFeed::atom($this->parameters['nickname'], $last_update, 10, $type, $nocache, true);
|
||||||
|
if (empty($feed)) {
|
||||||
|
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
System::httpExit($feed, Response::TYPE_ATOM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -360,7 +360,7 @@ class Group extends BaseModule
|
||||||
if ($change) {
|
if ($change) {
|
||||||
$tpl = Renderer::getMarkupTemplate('groupeditor.tpl');
|
$tpl = Renderer::getMarkupTemplate('groupeditor.tpl');
|
||||||
echo Renderer::replaceMacros($tpl, $context);
|
echo Renderer::replaceMacros($tpl, $context);
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Renderer::replaceMacros($tpl, $context);
|
return Renderer::replaceMacros($tpl, $context);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module\HTTPException;
|
namespace Friendica\Module\HTTPException;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
use Psr\Http\Message\ResponseInterface;
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
@ -47,7 +48,7 @@ class PageNotFound extends BaseModule
|
||||||
$queryString = $this->server['QUERY_STRING'];
|
$queryString = $this->server['QUERY_STRING'];
|
||||||
// Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
|
// Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
|
||||||
if (!empty($queryString) && preg_match('/{[0-9]}/', $queryString) !== 0) {
|
if (!empty($queryString) && preg_match('/{[0-9]}/', $queryString) !== 0) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
|
if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
|
||||||
|
|
|
@ -60,9 +60,9 @@ class Pin extends BaseModule
|
||||||
$pinned = !$item['featured'];
|
$pinned = !$item['featured'];
|
||||||
|
|
||||||
if ($pinned) {
|
if ($pinned) {
|
||||||
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
|
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED, local_user());
|
||||||
} else {
|
} else {
|
||||||
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED);
|
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED, local_user());
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if we've been passed a return path to redirect to
|
// See if we've been passed a return path to redirect to
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module;
|
namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
|
use Friendica\Core\Cache\Enum\Duration;
|
||||||
use Friendica\Core\Protocol;
|
use Friendica\Core\Protocol;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
|
@ -35,6 +36,8 @@ use Friendica\Model\User;
|
||||||
*/
|
*/
|
||||||
class NoScrape extends BaseModule
|
class NoScrape extends BaseModule
|
||||||
{
|
{
|
||||||
|
const CACHEKEY = 'noscrape:';
|
||||||
|
|
||||||
protected function rawContent(array $request = [])
|
protected function rawContent(array $request = [])
|
||||||
{
|
{
|
||||||
$a = DI::app();
|
$a = DI::app();
|
||||||
|
@ -55,6 +58,12 @@ class NoScrape extends BaseModule
|
||||||
System::jsonError(404, 'Profile not found');
|
System::jsonError(404, 'Profile not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$cachekey = self::CACHEKEY . $owner['uid'];
|
||||||
|
$result = DI::cache()->get($cachekey);
|
||||||
|
if (!is_null($result)) {
|
||||||
|
System::jsonExit($result);
|
||||||
|
}
|
||||||
|
|
||||||
$json_info = [
|
$json_info = [
|
||||||
'addr' => $owner['addr'],
|
'addr' => $owner['addr'],
|
||||||
'nick' => $which,
|
'nick' => $which,
|
||||||
|
@ -126,6 +135,8 @@ class NoScrape extends BaseModule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DI::cache()->set($cachekey, $json_info, Duration::DAY);
|
||||||
|
|
||||||
System::jsonExit($json_info);
|
System::jsonExit($json_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,6 +187,9 @@ class Ping extends BaseModule
|
||||||
$owner = User::getOwnerDataById(local_user());
|
$owner = User::getOwnerDataById(local_user());
|
||||||
|
|
||||||
$navNotifications = array_map(function (Entity\Notification $notification) use ($owner) {
|
$navNotifications = array_map(function (Entity\Notification $notification) use ($owner) {
|
||||||
|
if (!DI::notify()->NotifyOnDesktop($notification)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (($notification->type == Post\UserNotification::TYPE_NONE) && in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP])) {
|
if (($notification->type == Post\UserNotification::TYPE_NONE) && in_array($owner['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
use Friendica\Content;
|
use Friendica\Content;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Util\Strings;
|
use Friendica\Util\Strings;
|
||||||
|
|
||||||
|
@ -43,14 +44,14 @@ class Oembed extends BaseModule
|
||||||
if (DI::args()->getArgv()[1] == 'b2h') {
|
if (DI::args()->getArgv()[1] == 'b2h') {
|
||||||
$url = ["", trim(hex2bin($_REQUEST['url']))];
|
$url = ["", trim(hex2bin($_REQUEST['url']))];
|
||||||
echo Content\OEmbed::replaceCallback($url);
|
echo Content\OEmbed::replaceCallback($url);
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unused form: /oembed/h2b?text=...
|
// Unused form: /oembed/h2b?text=...
|
||||||
if (DI::args()->getArgv()[1] == 'h2b') {
|
if (DI::args()->getArgv()[1] == 'h2b') {
|
||||||
$text = trim(hex2bin($_REQUEST['text']));
|
$text = trim(hex2bin($_REQUEST['text']));
|
||||||
echo Content\OEmbed::HTML2BBCode($text);
|
echo Content\OEmbed::HTML2BBCode($text);
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: Replace with parameter from router
|
// @TODO: Replace with parameter from router
|
||||||
|
@ -68,6 +69,6 @@ class Oembed extends BaseModule
|
||||||
echo $j->html;
|
echo $j->html;
|
||||||
echo '</body></html>';
|
echo '</body></html>';
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module;
|
namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\APContact;
|
use Friendica\Model\APContact;
|
||||||
|
@ -164,8 +165,7 @@ class PermissionTooltip extends \Friendica\BaseModule
|
||||||
} else {
|
} else {
|
||||||
echo $o . $receivers;
|
echo $o . $receivers;
|
||||||
}
|
}
|
||||||
|
System::exit();
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,7 @@ use Friendica\Model\Post;
|
||||||
use Friendica\Model\Profile;
|
use Friendica\Model\Profile;
|
||||||
use Friendica\Core\Storage\Type\ExternalResource;
|
use Friendica\Core\Storage\Type\ExternalResource;
|
||||||
use Friendica\Core\Storage\Type\SystemResource;
|
use Friendica\Core\Storage\Type\SystemResource;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Core\Worker;
|
use Friendica\Core\Worker;
|
||||||
use Friendica\Model\User;
|
use Friendica\Model\User;
|
||||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||||
|
@ -224,7 +225,7 @@ class Photo extends BaseModule
|
||||||
'output' => number_format($output, 3), 'rest' => number_format($rest, 3)]);
|
'output' => number_format($output, 3), 'rest' => number_format($rest, 3)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getPhotoByid(int $id, $type, $customsize)
|
private static function getPhotoByid(int $id, $type, $customsize)
|
||||||
|
@ -304,10 +305,12 @@ class Photo extends BaseModule
|
||||||
$photo = MPhoto::selectFirst([], ['resource-id' => $resourceid], ['order' => ['scale']]);
|
$photo = MPhoto::selectFirst([], ['resource-id' => $resourceid], ['order' => ['scale']]);
|
||||||
if (!empty($photo)) {
|
if (!empty($photo)) {
|
||||||
return $photo;
|
return $photo;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
// We continue with the avatar link when the photo link is invalid
|
|
||||||
$url = $contact['avatar'];
|
$url = $contact['avatar'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$url = $contact['photo'];
|
||||||
|
}
|
||||||
} elseif (!empty($contact['avatar'])) {
|
} elseif (!empty($contact['avatar'])) {
|
||||||
$url = $contact['avatar'];
|
$url = $contact['avatar'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Profile extends BaseProfile
|
||||||
protected function rawContent(array $request = [])
|
protected function rawContent(array $request = [])
|
||||||
{
|
{
|
||||||
if (ActivityPub::isRequest()) {
|
if (ActivityPub::isRequest()) {
|
||||||
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $this->parameters['nickname']]);
|
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $this->parameters['nickname'], 'account_removed' => false]);
|
||||||
if (DBA::isResult($user)) {
|
if (DBA::isResult($user)) {
|
||||||
try {
|
try {
|
||||||
$data = ActivityPub\Transmitter::getProfile($user['uid']);
|
$data = ActivityPub\Transmitter::getProfile($user['uid']);
|
||||||
|
|
|
@ -202,6 +202,6 @@ class Proxy extends BaseModule
|
||||||
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
|
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
|
||||||
header('Cache-Control: max-age=31536000');
|
header('Cache-Control: max-age=31536000');
|
||||||
echo $img->asString();
|
echo $img->asString();
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
namespace Friendica\Module;
|
namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
|
use Friendica\Core\System;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default robots.txt
|
* Return the default robots.txt
|
||||||
|
@ -44,6 +45,6 @@ class RobotsTxt extends BaseModule
|
||||||
foreach ($allDisalloweds as $disallowed) {
|
foreach ($allDisalloweds as $disallowed) {
|
||||||
echo 'Disallow: ' . $disallowed . PHP_EOL;
|
echo 'Disallow: ' . $disallowed . PHP_EOL;
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ use Friendica\Model\Post;
|
||||||
use Friendica\Model\Tag;
|
use Friendica\Model\Tag;
|
||||||
use Friendica\Module\BaseSearch;
|
use Friendica\Module\BaseSearch;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Util\Network;
|
||||||
|
|
||||||
class Index extends BaseSearch
|
class Index extends BaseSearch
|
||||||
{
|
{
|
||||||
|
@ -226,6 +227,7 @@ class Index extends BaseSearch
|
||||||
*/
|
*/
|
||||||
private static function tryRedirectToProfile(string $search)
|
private static function tryRedirectToProfile(string $search)
|
||||||
{
|
{
|
||||||
|
$search = Network::convertToIdn($search);
|
||||||
$isUrl = !empty(parse_url($search, PHP_URL_SCHEME));
|
$isUrl = !empty(parse_url($search, PHP_URL_SCHEME));
|
||||||
$isAddr = (bool)preg_match('/^@?([a-z0-9.-_]+@[a-z0-9.-_:]+)$/i', trim($search), $matches);
|
$isAddr = (bool)preg_match('/^@?([a-z0-9.-_]+@[a-z0-9.-_:]+)$/i', trim($search), $matches);
|
||||||
|
|
||||||
|
@ -274,6 +276,8 @@ class Index extends BaseSearch
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$search = Network::convertToIdn($search);
|
||||||
|
|
||||||
if (local_user()) {
|
if (local_user()) {
|
||||||
// Post URL search
|
// Post URL search
|
||||||
$item_id = Item::fetchByLink($search, local_user());
|
$item_id = Item::fetchByLink($search, local_user());
|
||||||
|
|
|
@ -349,7 +349,7 @@ class Account extends BaseSettings
|
||||||
// "http" or "@" to be present in the string.
|
// "http" or "@" to be present in the string.
|
||||||
// All other fields from the row will be ignored
|
// All other fields from the row will be ignored
|
||||||
if ((strpos($csvRow[0], '@') !== false) || in_array(parse_url($csvRow[0], PHP_URL_SCHEME), ['http', 'https'])) {
|
if ((strpos($csvRow[0], '@') !== false) || in_array(parse_url($csvRow[0], PHP_URL_SCHEME), ['http', 'https'])) {
|
||||||
Worker::add(PRIORITY_LOW, 'AddContact', $_SESSION['uid'], $csvRow[0]);
|
Worker::add(PRIORITY_MEDIUM, 'AddContact', local_user(), $csvRow[0]);
|
||||||
} else {
|
} else {
|
||||||
Logger::notice('Invalid account', ['url' => $csvRow[0]]);
|
Logger::notice('Invalid account', ['url' => $csvRow[0]]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module\Settings;
|
||||||
|
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\Database\DBStructure;
|
use Friendica\Database\DBStructure;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
@ -112,8 +113,7 @@ class UserExport extends BaseSettings
|
||||||
self::exportContactsAsCSV(local_user());
|
self::exportContactsAsCSV(local_user());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
System::exit();
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
namespace Friendica\Module;
|
namespace Friendica\Module;
|
||||||
|
|
||||||
use Friendica\BaseModule;
|
use Friendica\BaseModule;
|
||||||
use Friendica\DI;
|
use Friendica\Core\System;
|
||||||
use Friendica\Util\Strings;
|
use Friendica\Util\Strings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +45,6 @@ class Theme extends BaseModule
|
||||||
if (file_exists("view/theme/$theme/style.php")) {
|
if (file_exists("view/theme/$theme/style.php")) {
|
||||||
require_once "view/theme/$theme/style.php";
|
require_once "view/theme/$theme/style.php";
|
||||||
}
|
}
|
||||||
|
System::exit();
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,6 @@ class ThemeDetails extends BaseModule
|
||||||
'credits' => $credits,
|
'credits' => $credits,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Network extends NetworkModule
|
||||||
protected function rawContent(array $request = [])
|
protected function rawContent(array $request = [])
|
||||||
{
|
{
|
||||||
if (!isset($_GET['p']) || !isset($_GET['item'])) {
|
if (!isset($_GET['p']) || !isset($_GET['item'])) {
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->parseRequest($_GET);
|
$this->parseRequest($_GET);
|
||||||
|
|
|
@ -25,7 +25,10 @@ use Friendica\App\BaseURL;
|
||||||
use Friendica\BaseFactory;
|
use Friendica\BaseFactory;
|
||||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||||
use Friendica\Contact\LocalRelationship\Repository\LocalRelationship;
|
use Friendica\Contact\LocalRelationship\Repository\LocalRelationship;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Content\Text\Plaintext;
|
use Friendica\Content\Text\Plaintext;
|
||||||
|
use Friendica\Core\Cache\Enum\Duration;
|
||||||
|
use Friendica\Core\Cache\Capability\ICanCache;
|
||||||
use Friendica\Core\L10n;
|
use Friendica\Core\L10n;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
|
@ -43,14 +46,17 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
||||||
private $l10n;
|
private $l10n;
|
||||||
/** @var LocalRelationship */
|
/** @var LocalRelationship */
|
||||||
private $localRelationshipRepo;
|
private $localRelationshipRepo;
|
||||||
|
/** @var ICanCache */
|
||||||
|
private $cache;
|
||||||
|
|
||||||
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Core\L10n $l10n, \Friendica\Contact\LocalRelationship\Repository\LocalRelationship $localRelationshipRepo, LoggerInterface $logger)
|
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Core\L10n $l10n, \Friendica\Contact\LocalRelationship\Repository\LocalRelationship $localRelationshipRepo, LoggerInterface $logger, ICanCache $cache)
|
||||||
{
|
{
|
||||||
parent::__construct($logger);
|
parent::__construct($logger);
|
||||||
|
|
||||||
$this->baseUrl = $baseUrl;
|
$this->baseUrl = $baseUrl;
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
$this->localRelationshipRepo = $localRelationshipRepo;
|
$this->localRelationshipRepo = $localRelationshipRepo;
|
||||||
|
$this->cache = $cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFromTableRow(array $row): Entity\Notification
|
public function createFromTableRow(array $row): Entity\Notification
|
||||||
|
@ -107,6 +113,12 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
||||||
{
|
{
|
||||||
$message = [];
|
$message = [];
|
||||||
|
|
||||||
|
$cachekey = 'Notification:' . $Notification->id;
|
||||||
|
$result = $this->cache->get($cachekey);
|
||||||
|
if (!is_null($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
$causer = $author = Contact::getById($Notification->actorId, ['id', 'name', 'url', 'contact-type', 'pending']);
|
$causer = $author = Contact::getById($Notification->actorId, ['id', 'name', 'url', 'contact-type', 'pending']);
|
||||||
if (empty($causer)) {
|
if (empty($causer)) {
|
||||||
$this->logger->info('Causer not found', ['contact' => $Notification->actorId]);
|
$this->logger->info('Causer not found', ['contact' => $Notification->actorId]);
|
||||||
|
@ -170,11 +182,10 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
||||||
|
|
||||||
$link = $this->baseUrl . '/display/' . urlencode($link_item['guid']);
|
$link = $this->baseUrl . '/display/' . urlencode($link_item['guid']);
|
||||||
|
|
||||||
$content = Plaintext::getPost($item, 70);
|
$body = BBCode::toPlaintext($item['body'], false);
|
||||||
if (!empty($content['text'])) {
|
$title = Plaintext::shorten($body, 70);
|
||||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
if (!empty($title)) {
|
||||||
} else {
|
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||||
$title = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->logger->debug('Got verb and type', ['verb' => $Notification->verb, 'type' => $Notification->type, 'causer' => $causer['id'], 'author' => $author['id'], 'item' => $item['id'], 'uid' => $Notification->uid]);
|
$this->logger->debug('Got verb and type', ['verb' => $Notification->verb, 'type' => $Notification->type, 'causer' => $causer['id'], 'author' => $author['id'], 'item' => $item['id'], 'uid' => $Notification->uid]);
|
||||||
|
@ -306,6 +317,7 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
||||||
'[url=' . $link . ']' . $title . '[/url]',
|
'[url=' . $link . ']' . $title . '[/url]',
|
||||||
'[url=' . $author['url'] . ']' . $author['name'] . '[/url]');
|
'[url=' . $author['url'] . ']' . $author['name'] . '[/url]');
|
||||||
$message['link'] = $link;
|
$message['link'] = $link;
|
||||||
|
$this->cache->set($cachekey, $message, Duration::HOUR);
|
||||||
} else {
|
} else {
|
||||||
$this->logger->debug('Unhandled notification', ['notification' => $Notification]);
|
$this->logger->debug('Unhandled notification', ['notification' => $Notification]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,10 @@ namespace Friendica\Navigation\Notifications\Repository;
|
||||||
|
|
||||||
use Friendica\App\BaseURL;
|
use Friendica\App\BaseURL;
|
||||||
use Friendica\BaseRepository;
|
use Friendica\BaseRepository;
|
||||||
|
use Friendica\Content\Text\BBCode;
|
||||||
use Friendica\Content\Text\Plaintext;
|
use Friendica\Content\Text\Plaintext;
|
||||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
|
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||||
use Friendica\Core\Hook;
|
use Friendica\Core\Hook;
|
||||||
use Friendica\Core\L10n;
|
use Friendica\Core\L10n;
|
||||||
use Friendica\Core\System;
|
use Friendica\Core\System;
|
||||||
|
@ -36,6 +38,7 @@ use Friendica\Navigation\Notifications\Entity;
|
||||||
use Friendica\Navigation\Notifications\Exception;
|
use Friendica\Navigation\Notifications\Exception;
|
||||||
use Friendica\Navigation\Notifications\Factory;
|
use Friendica\Navigation\Notifications\Factory;
|
||||||
use Friendica\Network\HTTPException;
|
use Friendica\Network\HTTPException;
|
||||||
|
use Friendica\Object\Api\Mastodon\Notification;
|
||||||
use Friendica\Protocol\Activity;
|
use Friendica\Protocol\Activity;
|
||||||
use Friendica\Util\DateTimeFormat;
|
use Friendica\Util\DateTimeFormat;
|
||||||
use Friendica\Util\Emailer;
|
use Friendica\Util\Emailer;
|
||||||
|
@ -58,6 +61,9 @@ class Notify extends BaseRepository
|
||||||
/** @var IManageConfigValues */
|
/** @var IManageConfigValues */
|
||||||
protected $config;
|
protected $config;
|
||||||
|
|
||||||
|
/** @var IManagePersonalConfigValues */
|
||||||
|
private $pConfig;
|
||||||
|
|
||||||
/** @var Emailer */
|
/** @var Emailer */
|
||||||
protected $emailer;
|
protected $emailer;
|
||||||
|
|
||||||
|
@ -66,11 +72,12 @@ class Notify extends BaseRepository
|
||||||
|
|
||||||
protected static $table_name = 'notify';
|
protected static $table_name = 'notify';
|
||||||
|
|
||||||
public function __construct(Database $database, LoggerInterface $logger, L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, Emailer $emailer, Factory\Notification $notification, Factory\Notify $factory = null)
|
public function __construct(Database $database, LoggerInterface $logger, L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, Emailer $emailer, Factory\Notification $notification, Factory\Notify $factory = null)
|
||||||
{
|
{
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
$this->baseUrl = $baseUrl;
|
$this->baseUrl = $baseUrl;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
$this->pConfig = $pConfig;
|
||||||
$this->emailer = $emailer;
|
$this->emailer = $emailer;
|
||||||
$this->notification = $notification;
|
$this->notification = $notification;
|
||||||
|
|
||||||
|
@ -301,11 +308,10 @@ class Notify extends BaseRepository
|
||||||
|
|
||||||
$item_post_type = Model\Item::postType($item, $l10n);
|
$item_post_type = Model\Item::postType($item, $l10n);
|
||||||
|
|
||||||
$content = Plaintext::getPost($item, 70);
|
$body = BBCode::toPlaintext($item['body'], false);
|
||||||
if (!empty($content['text'])) {
|
$title = Plaintext::shorten($body, 70);
|
||||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
if (!empty($title)) {
|
||||||
} else {
|
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||||
$title = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First go for the general message
|
// First go for the general message
|
||||||
|
@ -651,6 +657,27 @@ class Notify extends BaseRepository
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function NotifyOnDesktop(Entity\Notification $Notification, string $type = null): bool
|
||||||
|
{
|
||||||
|
if (is_null($type)) {
|
||||||
|
$type = \Friendica\Factory\Api\Mastodon\Notification::getType($Notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($type, [Notification::TYPE_RESHARE, Notification::TYPE_LIKE])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->pConfig->get($Notification->uid, 'system', 'notify_like') && ($type == Notification::TYPE_LIKE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->pConfig->get($Notification->uid, 'system', 'notify_announce') && ($type == Notification::TYPE_RESHARE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function createFromNotification(Entity\Notification $Notification)
|
public function createFromNotification(Entity\Notification $Notification)
|
||||||
{
|
{
|
||||||
$this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
|
$this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
|
||||||
|
@ -710,11 +737,10 @@ class Notify extends BaseRepository
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = Plaintext::getPost($item, 70);
|
$body = BBCode::toPlaintext($item['body'], false);
|
||||||
if (!empty($content['text'])) {
|
$title = Plaintext::shorten($body, 70);
|
||||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
if (!empty($title)) {
|
||||||
} else {
|
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||||
$title = $item['title'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some mail software relies on subject field for threading.
|
// Some mail software relies on subject field for threading.
|
||||||
|
|
|
@ -140,7 +140,7 @@ class HttpClient implements ICanSendHttpRequests
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (empty($conf[HttpClientOptions::HEADERS]['Accept'])) {
|
if (empty($conf[HttpClientOptions::HEADERS]['Accept']) && in_array($method, ['get', 'head'])) {
|
||||||
$this->logger->info('Accept header was missing, using default.', ['url' => $url, 'callstack' => System::callstack()]);
|
$this->logger->info('Accept header was missing, using default.', ['url' => $url, 'callstack' => System::callstack()]);
|
||||||
$conf[HttpClientOptions::HEADERS]['Accept'] = HttpClientAccept::DEFAULT;
|
$conf[HttpClientOptions::HEADERS]['Accept'] = HttpClientAccept::DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,8 @@ class Probe
|
||||||
// At first remove leading and trailing junk
|
// At first remove leading and trailing junk
|
||||||
$rawUri = trim($rawUri, "@#?:/ \t\n\r\0\x0B");
|
$rawUri = trim($rawUri, "@#?:/ \t\n\r\0\x0B");
|
||||||
|
|
||||||
|
$rawUri = Network::convertToIdn($rawUri);
|
||||||
|
|
||||||
$uri = new Uri($rawUri);
|
$uri = new Uri($rawUri);
|
||||||
if (!$uri->getScheme()) {
|
if (!$uri->getScheme()) {
|
||||||
return $uri->__toString();
|
return $uri->__toString();
|
||||||
|
@ -243,49 +245,6 @@ class Probe
|
||||||
return $lrdd;
|
return $lrdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform Webfinger lookup and return DFRN data
|
|
||||||
*
|
|
||||||
* Given an email style address, perform webfinger lookup and
|
|
||||||
* return the resulting DFRN profile URL, or if no DFRN profile URL
|
|
||||||
* is located, returns an OStatus subscription template (prefixed
|
|
||||||
* with the string 'stat:' to identify it as on OStatus template).
|
|
||||||
* If this isn't an email style address just return $webbie.
|
|
||||||
* Return an empty string if email-style addresses but webfinger fails,
|
|
||||||
* or if the resultant personal XRD doesn't contain a supported
|
|
||||||
* subscription/friend-request attribute.
|
|
||||||
*
|
|
||||||
* amended 7/9/2011 to return an hcard which could save potentially loading
|
|
||||||
* a lengthy content page to scrape dfrn attributes
|
|
||||||
*
|
|
||||||
* @param string $webbie Address that should be probed
|
|
||||||
* @param string $hcard_url Link to the hcard - is returned by reference
|
|
||||||
*
|
|
||||||
* @return string profile link
|
|
||||||
* @throws HTTPException\InternalServerErrorException
|
|
||||||
*/
|
|
||||||
public static function webfingerDfrn(string $webbie, string &$hcard_url)
|
|
||||||
{
|
|
||||||
$profile_link = '';
|
|
||||||
|
|
||||||
$links = self::lrdd($webbie);
|
|
||||||
Logger::debug('Result', ['url' => $webbie, 'links' => $links]);
|
|
||||||
if (!empty($links) && is_array($links)) {
|
|
||||||
foreach ($links as $link) {
|
|
||||||
if ($link['@attributes']['rel'] === ActivityNamespace::DFRN) {
|
|
||||||
$profile_link = $link['@attributes']['href'];
|
|
||||||
}
|
|
||||||
if (($link['@attributes']['rel'] === ActivityNamespace::OSTATUSSUB) && ($profile_link == "")) {
|
|
||||||
$profile_link = 'stat:'.$link['@attributes']['template'];
|
|
||||||
}
|
|
||||||
if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') {
|
|
||||||
$hcard_url = $link['@attributes']['href'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $profile_link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check an URI for LRDD data
|
* Check an URI for LRDD data
|
||||||
*
|
*
|
||||||
|
|
|
@ -159,6 +159,7 @@ class Image
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->valid = false;
|
$this->valid = false;
|
||||||
|
try {
|
||||||
$this->image = @imagecreatefromstring($data);
|
$this->image = @imagecreatefromstring($data);
|
||||||
if ($this->image !== false) {
|
if ($this->image !== false) {
|
||||||
$this->width = imagesx($this->image);
|
$this->width = imagesx($this->image);
|
||||||
|
@ -169,6 +170,14 @@ class Image
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $error) {
|
||||||
|
/** @see https://github.com/php/doc-en/commit/d09a881a8e9059d11e756ee59d75bf404d6941ed */
|
||||||
|
if (strstr($error->getMessage(), "gd-webp cannot allocate temporary buffer")) {
|
||||||
|
DI::logger()->notice('Image is probably animated and therefore unsupported', ['error' => $error]);
|
||||||
|
} else {
|
||||||
|
DI::logger()->warning('Unexpected throwable.', ['error' => $error]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,10 @@ use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Protocol;
|
use Friendica\Core\Protocol;
|
||||||
use Friendica\Core\Renderer;
|
use Friendica\Core\Renderer;
|
||||||
use Friendica\Core\Session;
|
use Friendica\Core\Session;
|
||||||
use Friendica\Database\DBA;
|
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Model\Item;
|
use Friendica\Model\Item;
|
||||||
|
use Friendica\Model\Photo;
|
||||||
use Friendica\Model\Post as PostModel;
|
use Friendica\Model\Post as PostModel;
|
||||||
use Friendica\Model\Tag;
|
use Friendica\Model\Tag;
|
||||||
use Friendica\Model\User;
|
use Friendica\Model\User;
|
||||||
|
@ -482,7 +482,7 @@ class Post
|
||||||
'profile_url' => $profile_link,
|
'profile_url' => $profile_link,
|
||||||
'name' => $profile_name,
|
'name' => $profile_name,
|
||||||
'item_photo_menu_html' => DI::contentItem()->photoMenu($item, $formSecurityToken),
|
'item_photo_menu_html' => DI::contentItem()->photoMenu($item, $formSecurityToken),
|
||||||
'thumb' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['author-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
'thumb' => DI::baseUrl()->remove(DI::contentItem()->getAuthorAvatar($item)),
|
||||||
'osparkle' => $osparkle,
|
'osparkle' => $osparkle,
|
||||||
'sparkle' => $sparkle,
|
'sparkle' => $sparkle,
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
@ -499,7 +499,7 @@ class Post
|
||||||
'shiny' => $shiny,
|
'shiny' => $shiny,
|
||||||
'owner_self' => $item['author-link'] == Session::get('my_url'),
|
'owner_self' => $item['author-link'] == Session::get('my_url'),
|
||||||
'owner_url' => $this->getOwnerUrl(),
|
'owner_url' => $this->getOwnerUrl(),
|
||||||
'owner_photo' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
'owner_photo' => DI::baseUrl()->remove(DI::contentItem()->getOwnerAvatar($item)),
|
||||||
'owner_name' => $this->getOwnerName(),
|
'owner_name' => $this->getOwnerName(),
|
||||||
'plink' => Item::getPlink($item),
|
'plink' => Item::getPlink($item),
|
||||||
'browsershare' => $browsershare,
|
'browsershare' => $browsershare,
|
||||||
|
@ -529,8 +529,8 @@ class Post
|
||||||
'thread_level' => $thread_level,
|
'thread_level' => $thread_level,
|
||||||
'edited' => $edited,
|
'edited' => $edited,
|
||||||
'network' => $item["network"],
|
'network' => $item["network"],
|
||||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network']),
|
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network'], $item['author-gsid']),
|
||||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
|
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link'], $item['author-gsid']),
|
||||||
'received' => $item['received'],
|
'received' => $item['received'],
|
||||||
'commented' => $item['commented'],
|
'commented' => $item['commented'],
|
||||||
'created_date' => $item['created'],
|
'created_date' => $item['created'],
|
||||||
|
@ -898,12 +898,7 @@ class Post
|
||||||
}
|
}
|
||||||
|
|
||||||
$owner = User::getOwnerDataById($a->getLoggedInUserId());
|
$owner = User::getOwnerDataById($a->getLoggedInUserId());
|
||||||
|
$item = $this->getData();
|
||||||
$item = PostModel::selectFirst(['author-addr', 'uri-id', 'network', 'gravity', 'content-warning'], ['id' => $this->getId()]);
|
|
||||||
if (!DBA::isResult($item) || empty($item['author-addr'])) {
|
|
||||||
// Should not happen
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($item['content-warning']) && Feature::isEnabled(local_user(), 'add_abstract')) {
|
if (!empty($item['content-warning']) && Feature::isEnabled(local_user(), 'add_abstract')) {
|
||||||
$text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n";
|
$text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n";
|
||||||
|
@ -967,7 +962,7 @@ class Post
|
||||||
$uid = $conv->getProfileOwner();
|
$uid = $conv->getProfileOwner();
|
||||||
$parent_uid = $this->getDataValue('uid');
|
$parent_uid = $this->getDataValue('uid');
|
||||||
|
|
||||||
$contact = Contact::getById($a->getContactId());
|
$owner = User::getOwnerDataById($a->getLoggedInUserId());
|
||||||
|
|
||||||
$default_text = $this->getDefaultText();
|
$default_text = $this->getDefaultText();
|
||||||
|
|
||||||
|
@ -986,9 +981,9 @@ class Post
|
||||||
'$qcomment' => $qcomment,
|
'$qcomment' => $qcomment,
|
||||||
'$default' => $default_text,
|
'$default' => $default_text,
|
||||||
'$profile_uid' => $uid,
|
'$profile_uid' => $uid,
|
||||||
'$mylink' => DI::baseUrl()->remove($contact['url'] ?? ''),
|
'$mylink' => DI::baseUrl()->remove($owner['url'] ?? ''),
|
||||||
'$mytitle' => DI::l10n()->t('This is you'),
|
'$mytitle' => DI::l10n()->t('This is you'),
|
||||||
'$myphoto' => DI::baseUrl()->remove($contact['thumb'] ?? ''),
|
'$myphoto' => DI::baseUrl()->remove($owner['thumb'] ?? ''),
|
||||||
'$comment' => DI::l10n()->t('Comment'),
|
'$comment' => DI::l10n()->t('Comment'),
|
||||||
'$submit' => DI::l10n()->t('Submit'),
|
'$submit' => DI::l10n()->t('Submit'),
|
||||||
'$loading' => DI::l10n()->t('Loading...'),
|
'$loading' => DI::l10n()->t('Loading...'),
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2022, 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\Protocol\ActivityPub;
|
||||||
|
|
||||||
|
use Friendica\Core\Logger;
|
||||||
|
use Friendica\Database\DBA;
|
||||||
|
use Friendica\DI;
|
||||||
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\GServer;
|
||||||
|
use Friendica\Model\Post;
|
||||||
|
use Friendica\Protocol\ActivityPub;
|
||||||
|
use Friendica\Util\HTTPSignature;
|
||||||
|
use Friendica\Worker\Delivery as WorkerDelivery;
|
||||||
|
|
||||||
|
class Delivery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Deliver posts to the given inbox
|
||||||
|
*
|
||||||
|
* @param string $inbox
|
||||||
|
* @return array with the elements "success" and "uri_ids" of the failed posts
|
||||||
|
*/
|
||||||
|
public static function deliver(string $inbox): array
|
||||||
|
{
|
||||||
|
$uri_ids = [];
|
||||||
|
$posts = Post\Delivery::selectForInbox($inbox);
|
||||||
|
$serverfail = false;
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
if (!$serverfail) {
|
||||||
|
$result = self::deliverToInbox($post['command'], 0, $inbox, $post['uid'], $post['receivers'], $post['uri-id']);
|
||||||
|
|
||||||
|
if ($result['serverfailure']) {
|
||||||
|
// In a timeout situation we assume that every delivery to that inbox will time out.
|
||||||
|
// So we set the flag and try all deliveries at a later time.
|
||||||
|
Logger::info('Inbox delivery has a server failure', ['inbox' => $inbox]);
|
||||||
|
$serverfail = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($serverfail || (!$result['success'] && !$result['drop'])) {
|
||||||
|
$uri_ids[] = $post['uri-id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::debug('Inbox delivery done', ['inbox' => $inbox, 'posts' => count($posts), 'failed' => count($uri_ids), 'serverfailure' => $serverfail]);
|
||||||
|
return ['success' => empty($uri_ids), 'uri_ids' => $uri_ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver the given post to the given inbox
|
||||||
|
*
|
||||||
|
* @param string $cmd
|
||||||
|
* @param integer $item_id
|
||||||
|
* @param string $inbox
|
||||||
|
* @param integer $uid
|
||||||
|
* @param array $receivers
|
||||||
|
* @param integer $uri_id
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function deliverToInbox(string $cmd, int $item_id, string $inbox, int $uid, array $receivers, int $uri_id): array
|
||||||
|
{
|
||||||
|
if (empty($item_id) && !empty($uri_id) && !empty($uid)) {
|
||||||
|
$item = Post::selectFirst(['id', 'parent', 'origin'], ['uri-id' => $uri_id, 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||||
|
if (empty($item['id'])) {
|
||||||
|
Logger::notice('Item not found, removing delivery', ['uri-id' => $uri_id, 'uid' => $uid, 'cmd' => $cmd, 'inbox' => $inbox]);
|
||||||
|
Post\Delivery::remove($uri_id, $inbox);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$item_id = $item['id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = true;
|
||||||
|
$serverfail = false;
|
||||||
|
$drop = false;
|
||||||
|
|
||||||
|
if ($cmd == WorkerDelivery::MAIL) {
|
||||||
|
$data = ActivityPub\Transmitter::createActivityFromMail($item_id);
|
||||||
|
if (!empty($data)) {
|
||||||
|
$success = HTTPSignature::transmit($data, $inbox, $uid);
|
||||||
|
}
|
||||||
|
} elseif ($cmd == WorkerDelivery::SUGGESTION) {
|
||||||
|
$success = ActivityPub\Transmitter::sendContactSuggestion($uid, $inbox, $item_id);
|
||||||
|
} elseif ($cmd == WorkerDelivery::RELOCATION) {
|
||||||
|
// @todo Implementation pending
|
||||||
|
} elseif ($cmd == WorkerDelivery::POKE) {
|
||||||
|
// Implementation not planned
|
||||||
|
} elseif ($cmd == WorkerDelivery::REMOVAL) {
|
||||||
|
$success = ActivityPub\Transmitter::sendProfileDeletion($uid, $inbox);
|
||||||
|
} elseif ($cmd == WorkerDelivery::PROFILEUPDATE) {
|
||||||
|
$success = ActivityPub\Transmitter::sendProfileUpdate($uid, $inbox);
|
||||||
|
} else {
|
||||||
|
$data = ActivityPub\Transmitter::createCachedActivityFromItem($item_id);
|
||||||
|
if (!empty($data)) {
|
||||||
|
$timestamp = microtime(true);
|
||||||
|
$response = HTTPSignature::post($data, $inbox, $uid);
|
||||||
|
$runtime = microtime(true) - $timestamp;
|
||||||
|
$success = $response->isSuccess();
|
||||||
|
$serverfail = $response->isTimeout();
|
||||||
|
if (!$success) {
|
||||||
|
// 5xx errors are problems on the server. We don't need to continue delivery then.
|
||||||
|
if (!$serverfail && ($response->getReturnCode() >= 500) && ($response->getReturnCode() <= 599)) {
|
||||||
|
$serverfail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 404 means that the inbox doesn't exist. We can stop the delivery here.
|
||||||
|
if (!$serverfail && ($response->getReturnCode() == 404)) {
|
||||||
|
$serverfail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xrd_timeout = DI::config()->get('system', 'xrd_timeout');
|
||||||
|
if (!$serverfail && $xrd_timeout && ($runtime > $xrd_timeout)) {
|
||||||
|
$serverfail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$curl_timeout = DI::config()->get('system', 'curl_timeout');
|
||||||
|
if (!$serverfail && $curl_timeout && ($runtime > $curl_timeout)) {
|
||||||
|
$serverfail = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resubscribe to relay server upon client error
|
||||||
|
if (!$serverfail && ($response->getReturnCode() >= 400) && ($response->getReturnCode() <= 499)) {
|
||||||
|
$actor = self:: fetchActorForRelayInbox($inbox);
|
||||||
|
if (!empty($actor)) {
|
||||||
|
$drop = !ActivityPub\Transmitter::sendRelayFollow($actor);
|
||||||
|
Logger::notice('Resubscribed to relay', ['url' => $actor, 'success' => !$drop]);
|
||||||
|
} elseif ($cmd = WorkerDelivery::DELETION) {
|
||||||
|
// Remote systems not always accept our deletion requests, so we drop them if rejected.
|
||||||
|
// Situation is: In Friendica we allow the thread owner to delete foreign comments to their thread.
|
||||||
|
// Most AP systems don't allow this, so they will reject the deletion request.
|
||||||
|
$drop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info('Delivery failed', ['retcode' => $response->getReturnCode(), 'serverfailure' => $serverfail, 'drop' => $drop, 'runtime' => round($runtime, 3), 'uri-id' => $uri_id, 'uid' => $uid, 'item_id' => $item_id, 'cmd' => $cmd, 'inbox' => $inbox]);
|
||||||
|
}
|
||||||
|
if ($uri_id) {
|
||||||
|
if ($success) {
|
||||||
|
Post\Delivery::remove($uri_id, $inbox);
|
||||||
|
} else {
|
||||||
|
Post\Delivery::incrementFailed($uri_id, $inbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self::setSuccess($receivers, $success);
|
||||||
|
|
||||||
|
Logger::debug('Delivered', ['uri-id' => $uri_id, 'uid' => $uid, 'item_id' => $item_id, 'cmd' => $cmd, 'inbox' => $inbox, 'success' => $success, 'serverfailure' => $serverfail, 'drop' => $drop]);
|
||||||
|
|
||||||
|
if (($success || $drop) && in_array($cmd, [WorkerDelivery::POST])) {
|
||||||
|
Post\DeliveryData::incrementQueueDone($uri_id, Post\DeliveryData::ACTIVITYPUB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => $success, 'serverfailure' => $serverfail, 'drop' => $drop];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the actor of the given inbox of an relay server
|
||||||
|
*
|
||||||
|
* @param string $inbox
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function fetchActorForRelayInbox(string $inbox): string
|
||||||
|
{
|
||||||
|
$apcontact = DBA::selectFirst('apcontact', ['url'], ["`sharedinbox` = ? AND `type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)",
|
||||||
|
$inbox, 'Application', 0, Contact::FRIEND]);
|
||||||
|
return $apcontact['url'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mark or unmark the given receivers for archival upon succoess
|
||||||
|
*
|
||||||
|
* @param array $receivers
|
||||||
|
* @param boolean $success
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function setSuccess(array $receivers, bool $success)
|
||||||
|
{
|
||||||
|
$gsid = null;
|
||||||
|
|
||||||
|
foreach ($receivers as $receiver) {
|
||||||
|
$contact = Contact::getById($receiver);
|
||||||
|
if (empty($contact)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$gsid = $gsid ?: $contact['gsid'];
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
Contact::unmarkForArchival($contact);
|
||||||
|
} else {
|
||||||
|
Contact::markForArchival($contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($gsid)) {
|
||||||
|
GServer::setProtocol($gsid, Post\DeliveryData::ACTIVITYPUB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -170,7 +170,7 @@ class Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($activity['question']['end-time'])) {
|
if (!empty($activity['question']['end-time'])) {
|
||||||
$question['end-time'] = $activity['question']['end-time'];
|
$question['end-time'] = DateTimeFormat::utc($activity['question']['end-time']);
|
||||||
}
|
}
|
||||||
|
|
||||||
Post\Question::update($item['uri-id'], $question);
|
Post\Question::update($item['uri-id'], $question);
|
||||||
|
@ -215,6 +215,7 @@ class Processor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Post\History::add($item['uri-id'], $item);
|
||||||
Item::update($item, ['uri' => $activity['id']]);
|
Item::update($item, ['uri' => $activity['id']]);
|
||||||
|
|
||||||
if ($activity['object_type'] == 'as:Event') {
|
if ($activity['object_type'] == 'as:Event') {
|
||||||
|
@ -238,8 +239,12 @@ class Processor
|
||||||
$event['edited'] = DateTimeFormat::utc($activity['updated']);
|
$event['edited'] = DateTimeFormat::utc($activity['updated']);
|
||||||
$event['summary'] = HTML::toBBCode($activity['name']);
|
$event['summary'] = HTML::toBBCode($activity['name']);
|
||||||
$event['desc'] = HTML::toBBCode($activity['content']);
|
$event['desc'] = HTML::toBBCode($activity['content']);
|
||||||
$event['start'] = $activity['start-time'];
|
if (!empty($activity['start-time'])) {
|
||||||
$event['finish'] = $activity['end-time'];
|
$event['start'] = DateTimeFormat::utc($activity['start-time']);
|
||||||
|
}
|
||||||
|
if (!empty($activity['end-time'])) {
|
||||||
|
$event['finish'] = DateTimeFormat::utc($activity['end-time']);
|
||||||
|
}
|
||||||
$event['nofinish'] = empty($event['finish']);
|
$event['nofinish'] = empty($event['finish']);
|
||||||
$event['location'] = $activity['location'];
|
$event['location'] = $activity['location'];
|
||||||
|
|
||||||
|
@ -558,8 +563,12 @@ class Processor
|
||||||
{
|
{
|
||||||
$event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']);
|
$event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']);
|
||||||
$event['desc'] = HTML::toBBCode($activity['content']);
|
$event['desc'] = HTML::toBBCode($activity['content']);
|
||||||
$event['start'] = $activity['start-time'];
|
if (!empty($activity['start-time'])) {
|
||||||
$event['finish'] = $activity['end-time'];
|
$event['start'] = DateTimeFormat::utc($activity['start-time']);
|
||||||
|
}
|
||||||
|
if (!empty($activity['end-time'])) {
|
||||||
|
$event['finish'] = DateTimeFormat::utc($activity['end-time']);
|
||||||
|
}
|
||||||
$event['nofinish'] = empty($event['finish']);
|
$event['nofinish'] = empty($event['finish']);
|
||||||
$event['location'] = $activity['location'];
|
$event['location'] = $activity['location'];
|
||||||
$event['cid'] = $item['contact-id'];
|
$event['cid'] = $item['contact-id'];
|
||||||
|
|
|
@ -938,7 +938,7 @@ class Receiver
|
||||||
|
|
||||||
// Fetch the receivers for the public and the followers collection
|
// Fetch the receivers for the public and the followers collection
|
||||||
if ((($receiver == $followers) || (($receiver == self::PUBLIC_COLLECTION) && !$is_forum)) && !empty($actor)) {
|
if ((($receiver == $followers) || (($receiver == self::PUBLIC_COLLECTION) && !$is_forum)) && !empty($actor)) {
|
||||||
$receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target);
|
$receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target, $profile);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1000,15 +1000,27 @@ class Receiver
|
||||||
* @param array $tags
|
* @param array $tags
|
||||||
* @param array $receivers
|
* @param array $receivers
|
||||||
* @param integer $target_type
|
* @param integer $target_type
|
||||||
|
* @param array $profile
|
||||||
*
|
*
|
||||||
* @return array with receivers (user id)
|
* @return array with receivers (user id)
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private static function getReceiverForActor($actor, $tags, $receivers, $target_type)
|
private static function getReceiverForActor($actor, $tags, $receivers, $target_type, $profile)
|
||||||
{
|
{
|
||||||
$basecondition = ['rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
|
$basecondition = ['rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
|
||||||
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false];
|
'network' => Protocol::FEDERATED, 'archive' => false, 'pending' => false];
|
||||||
|
|
||||||
|
if (!empty($profile['uri-id'])) {
|
||||||
|
$condition = DBA::mergeConditions($basecondition, ["`uri-id` = ? AND `uid` != ?", $profile['uri-id'], 0]);
|
||||||
|
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
|
||||||
|
while ($contact = DBA::fetch($contacts)) {
|
||||||
|
if (empty($receivers[$contact['uid']]) && self::isValidReceiverForActor($contact, $tags)) {
|
||||||
|
$receivers[$contact['uid']] = ['uid' => $contact['uid'], 'type' => $target_type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBA::close($contacts);
|
||||||
|
} else {
|
||||||
|
// This part will only be called while post update 1426 wasn't finished
|
||||||
$condition = DBA::mergeConditions($basecondition, ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($actor), 0]);
|
$condition = DBA::mergeConditions($basecondition, ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($actor), 0]);
|
||||||
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
|
$contacts = DBA::select('contact', ['uid', 'rel'], $condition);
|
||||||
while ($contact = DBA::fetch($contacts)) {
|
while ($contact = DBA::fetch($contacts)) {
|
||||||
|
@ -1027,6 +1039,7 @@ class Receiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DBA::close($contacts);
|
DBA::close($contacts);
|
||||||
|
}
|
||||||
return $receivers;
|
return $receivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ use Friendica\Protocol\ActivityPub;
|
||||||
use Friendica\Protocol\Relay;
|
use Friendica\Protocol\Relay;
|
||||||
use Friendica\Util\DateTimeFormat;
|
use Friendica\Util\DateTimeFormat;
|
||||||
use Friendica\Util\HTTPSignature;
|
use Friendica\Util\HTTPSignature;
|
||||||
use Friendica\Util\JsonLD;
|
|
||||||
use Friendica\Util\LDSignature;
|
use Friendica\Util\LDSignature;
|
||||||
use Friendica\Util\Map;
|
use Friendica\Util\Map;
|
||||||
use Friendica\Util\Network;
|
use Friendica\Util\Network;
|
||||||
|
@ -59,6 +58,10 @@ use Friendica\Util\XML;
|
||||||
*/
|
*/
|
||||||
class Transmitter
|
class Transmitter
|
||||||
{
|
{
|
||||||
|
const CACHEKEY_FEATURED = 'transmitter:getFeatured:';
|
||||||
|
const CACHEKEY_CONTACTS = 'transmitter:getContacts:';
|
||||||
|
const CACHEKEY_OUTBOX = 'transmitter:getOutbox:';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add relay servers to the list of inboxes
|
* Add relay servers to the list of inboxes
|
||||||
*
|
*
|
||||||
|
@ -151,12 +154,21 @@ class Transmitter
|
||||||
* @param string $module The name of the relevant AP endpoint module (followers|following)
|
* @param string $module The name of the relevant AP endpoint module (followers|following)
|
||||||
* @param integer $page Page number
|
* @param integer $page Page number
|
||||||
* @param string $requester URL of the requester
|
* @param string $requester URL of the requester
|
||||||
|
* @param boolean $nocache Wether to bypass caching
|
||||||
*
|
*
|
||||||
* @return array of owners
|
* @return array of owners
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function getContacts(array $owner, array $rel, string $module, int $page = null, string $requester = null)
|
public static function getContacts(array $owner, array $rel, string $module, int $page = null, string $requester = null, $nocache = false)
|
||||||
{
|
{
|
||||||
|
if (empty($page)) {
|
||||||
|
$cachekey = self::CACHEKEY_CONTACTS . $module . ':'. $owner['uid'];
|
||||||
|
$result = DI::cache()->get($cachekey);
|
||||||
|
if (!$nocache && !is_null($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$parameters = [
|
$parameters = [
|
||||||
'rel' => $rel,
|
'rel' => $rel,
|
||||||
'uid' => $owner['uid'],
|
'uid' => $owner['uid'],
|
||||||
|
@ -192,6 +204,10 @@ class Transmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$show_contacts) {
|
if (!$show_contacts) {
|
||||||
|
if (!empty($cachekey)) {
|
||||||
|
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +232,10 @@ class Transmitter
|
||||||
$data['orderedItems'] = $list;
|
$data['orderedItems'] = $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($cachekey)) {
|
||||||
|
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,13 +245,22 @@ class Transmitter
|
||||||
* @param array $owner Owner array
|
* @param array $owner Owner array
|
||||||
* @param integer $page Page number
|
* @param integer $page Page number
|
||||||
* @param string $requester URL of requesting account
|
* @param string $requester URL of requesting account
|
||||||
|
* @param boolean $nocache Wether to bypass caching
|
||||||
*
|
*
|
||||||
* @return array of posts
|
* @return array of posts
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
* @throws \ImagickException
|
* @throws \ImagickException
|
||||||
*/
|
*/
|
||||||
public static function getOutbox(array $owner, int $page = null, string $requester = '')
|
public static function getOutbox(array $owner, int $page = null, string $requester = '', $nocache = false)
|
||||||
{
|
{
|
||||||
|
if (empty($page)) {
|
||||||
|
$cachekey = self::CACHEKEY_OUTBOX . $owner['uid'];
|
||||||
|
$result = DI::cache()->get($cachekey);
|
||||||
|
if (!$nocache && !is_null($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED]];
|
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED]];
|
||||||
|
|
||||||
if (!empty($requester)) {
|
if (!empty($requester)) {
|
||||||
|
@ -293,6 +322,10 @@ class Transmitter
|
||||||
$data['orderedItems'] = $list;
|
$data['orderedItems'] = $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($cachekey)) {
|
||||||
|
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||||
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,19 +334,30 @@ class Transmitter
|
||||||
*
|
*
|
||||||
* @param array $owner Owner array
|
* @param array $owner Owner array
|
||||||
* @param integer $page Page number
|
* @param integer $page Page number
|
||||||
|
* @param boolean $nocache Wether to bypass caching
|
||||||
*
|
*
|
||||||
* @return array of posts
|
* @return array of posts
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
* @throws \ImagickException
|
* @throws \ImagickException
|
||||||
*/
|
*/
|
||||||
public static function getFeatured(array $owner, int $page = null)
|
public static function getFeatured(array $owner, int $page = null, $nocache = false)
|
||||||
{
|
{
|
||||||
|
if (empty($page)) {
|
||||||
|
$cachekey = self::CACHEKEY_FEATURED . $owner['uid'];
|
||||||
|
$result = DI::cache()->get($cachekey);
|
||||||
|
if (!$nocache && !is_null($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$owner_cid = Contact::getIdForURL($owner['url'], 0, false);
|
||||||
|
|
||||||
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
||||||
Contact::getIdForURL($owner['url'], 0, false), Post\Collection::FEATURED];
|
$owner_cid, Post\Collection::FEATURED];
|
||||||
|
|
||||||
$condition = DBA::mergeConditions($condition,
|
$condition = DBA::mergeConditions($condition,
|
||||||
['uid' => $owner['uid'],
|
['uid' => $owner['uid'],
|
||||||
'author-id' => Contact::getIdForURL($owner['url'], 0, false),
|
'author-id' => $owner_cid,
|
||||||
'private' => [Item::PUBLIC, Item::UNLISTED],
|
'private' => [Item::PUBLIC, Item::UNLISTED],
|
||||||
'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
|
'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
|
||||||
'network' => Protocol::FEDERATED,
|
'network' => Protocol::FEDERATED,
|
||||||
|
@ -334,12 +378,13 @@ class Transmitter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($page)) {
|
if (empty($page)) {
|
||||||
$data['first'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=1';
|
$items = Post::select(['id'], $condition, ['limit' => 20, 'order' => ['created' => true]]);
|
||||||
} else {
|
} else {
|
||||||
$data['type'] = 'OrderedCollectionPage';
|
$data['type'] = 'OrderedCollectionPage';
|
||||||
|
$items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
|
||||||
|
}
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
$items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
|
|
||||||
while ($item = Post::fetch($items)) {
|
while ($item = Post::fetch($items)) {
|
||||||
$activity = self::createActivityFromItem($item['id'], true);
|
$activity = self::createActivityFromItem($item['id'], true);
|
||||||
$activity['type'] = $activity['type'] == 'Update' ? 'Create' : $activity['type'];
|
$activity['type'] = $activity['type'] == 'Update' ? 'Create' : $activity['type'];
|
||||||
|
@ -355,9 +400,14 @@ class Transmitter
|
||||||
$data['next'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=' . ($page + 1);
|
$data['next'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=' . ($page + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($page)) {
|
||||||
$data['partOf'] = DI::baseUrl() . '/featured/' . $owner['nickname'];
|
$data['partOf'] = DI::baseUrl() . '/featured/' . $owner['nickname'];
|
||||||
|
}
|
||||||
|
|
||||||
$data['orderedItems'] = $list;
|
$data['orderedItems'] = $list;
|
||||||
|
|
||||||
|
if (!empty($cachekey)) {
|
||||||
|
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -341,4 +341,15 @@ class Relay
|
||||||
// It should never happen that we arrive here
|
// It should never happen that we arrive here
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resubscribe to all relay servers
|
||||||
|
*/
|
||||||
|
public static function reSubscribe()
|
||||||
|
{
|
||||||
|
foreach (self::getList() as $server) {
|
||||||
|
$success = ActivityPub\Transmitter::sendRelayFollow($server['url']);
|
||||||
|
Logger::debug('Resubscribed', ['profile' => $server['url'], 'success' => $success]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,9 @@ use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\APContact;
|
use Friendica\Model\APContact;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Model\ItemURI;
|
||||||
use Friendica\Model\User;
|
use Friendica\Model\User;
|
||||||
|
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
|
||||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||||
|
|
||||||
|
@ -263,21 +265,21 @@ class HTTPSignature
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmit given data to a target for a user
|
* Post given data to a target for a user, returns the result class
|
||||||
*
|
*
|
||||||
* @param array $data Data that is about to be send
|
* @param array $data Data that is about to be send
|
||||||
* @param string $target The URL of the inbox
|
* @param string $target The URL of the inbox
|
||||||
* @param integer $uid User id of the sender
|
* @param integer $uid User id of the sender
|
||||||
*
|
*
|
||||||
* @return boolean Was the transmission successful?
|
* @return ICanHandleHttpResponses
|
||||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
*/
|
*/
|
||||||
public static function transmit($data, $target, $uid)
|
public static function post(array $data, string $target, int $uid): ICanHandleHttpResponses
|
||||||
{
|
{
|
||||||
$owner = User::getOwnerDataById($uid);
|
$owner = User::getOwnerDataById($uid);
|
||||||
|
|
||||||
if (!$owner) {
|
if (!$owner) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$content = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
$content = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
@ -304,16 +306,32 @@ class HTTPSignature
|
||||||
|
|
||||||
$headers['Content-Type'] = 'application/activity+json';
|
$headers['Content-Type'] = 'application/activity+json';
|
||||||
|
|
||||||
$postResult = DI::httpClient()->post($target, $content, $headers);
|
$postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout'));
|
||||||
$return_code = $postResult->getReturnCode();
|
$return_code = $postResult->getReturnCode();
|
||||||
|
|
||||||
Logger::info('Transmit to ' . $target . ' returned ' . $return_code);
|
Logger::info('Transmit to ' . $target . ' returned ' . $return_code);
|
||||||
|
|
||||||
$success = ($return_code >= 200) && ($return_code <= 299);
|
self::setInboxStatus($target, ($return_code >= 200) && ($return_code <= 299));
|
||||||
|
|
||||||
self::setInboxStatus($target, $success);
|
return $postResult;
|
||||||
|
}
|
||||||
|
|
||||||
return $success;
|
/**
|
||||||
|
* Transmit given data to a target for a user
|
||||||
|
*
|
||||||
|
* @param array $data Data that is about to be send
|
||||||
|
* @param string $target The URL of the inbox
|
||||||
|
* @param integer $uid User id of the sender
|
||||||
|
*
|
||||||
|
* @return boolean Was the transmission successful?
|
||||||
|
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function transmit(array $data, string $target, int $uid): bool
|
||||||
|
{
|
||||||
|
$postResult = self::post($data, $target, $uid);
|
||||||
|
$return_code = $postResult->getReturnCode();
|
||||||
|
|
||||||
|
return ($return_code >= 200) && ($return_code <= 299);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,7 +347,7 @@ class HTTPSignature
|
||||||
|
|
||||||
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
||||||
if (!DBA::isResult($status)) {
|
if (!DBA::isResult($status)) {
|
||||||
DBA::insert('inbox-status', ['url' => $url, 'created' => $now, 'shared' => $shared], Database::INSERT_IGNORE);
|
DBA::insert('inbox-status', ['url' => $url, 'uri-id' => ItemURI::getIdByURI($url), 'created' => $now, 'shared' => $shared], Database::INSERT_IGNORE);
|
||||||
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,6 +387,10 @@ class HTTPSignature
|
||||||
$fields['archive'] = false;
|
$fields['archive'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (empty($status['uri-id'])) {
|
||||||
|
$fields['uri-id'] = ItemURI::getIdByURI($url);
|
||||||
|
}
|
||||||
|
|
||||||
DBA::update('inbox-status', $fields, ['url' => $url]);
|
DBA::update('inbox-status', $fields, ['url' => $url]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace Friendica\Util;
|
||||||
use Friendica\Core\Cache\Enum\Duration;
|
use Friendica\Core\Cache\Enum\Duration;
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Friendica\Core\System;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +70,7 @@ class JsonLD
|
||||||
|
|
||||||
if ($recursion > 5) {
|
if ($recursion > 5) {
|
||||||
Logger::error('jsonld bomb detected at: ' . $url);
|
Logger::error('jsonld bomb detected at: ' . $url);
|
||||||
exit();
|
System::exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = DI::cache()->get('documentLoader:' . $url);
|
$result = DI::cache()->get('documentLoader:' . $url);
|
||||||
|
|
|
@ -26,6 +26,7 @@ use Friendica\Core\Logger;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
use Friendica\Network\HTTPException\NotModifiedException;
|
use Friendica\Network\HTTPException\NotModifiedException;
|
||||||
|
use GuzzleHttp\Psr7\Uri;
|
||||||
|
|
||||||
class Network
|
class Network
|
||||||
{
|
{
|
||||||
|
@ -436,7 +437,7 @@ class Network
|
||||||
* @param array $parsed URL parts
|
* @param array $parsed URL parts
|
||||||
*
|
*
|
||||||
* @return string The glued URL.
|
* @return string The glued URL.
|
||||||
* @deprecated since version 2021.12, use a UriInterface object like GuzzleHttp\Psr7\Uri instead
|
* @deprecated since version 2021.12, use GuzzleHttp\Psr7\Uri::fromParts($parts) instead
|
||||||
*/
|
*/
|
||||||
public static function unparseURL(array $parsed)
|
public static function unparseURL(array $parsed)
|
||||||
{
|
{
|
||||||
|
@ -462,6 +463,29 @@ class Network
|
||||||
(strlen($fragment) ? "#".$fragment : '');
|
(strlen($fragment) ? "#".$fragment : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an URI to an IDN compatible URI
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertToIdn(string $uri): string
|
||||||
|
{
|
||||||
|
$parts = parse_url($uri);
|
||||||
|
if (!empty($parts['scheme']) && !empty($parts['host'])) {
|
||||||
|
$parts['host'] = idn_to_ascii($parts['host']);
|
||||||
|
$uri = Uri::fromParts($parts);
|
||||||
|
} else {
|
||||||
|
$parts = explode('@', $uri);
|
||||||
|
if (count($parts) == 2) {
|
||||||
|
$uri = $parts[0] . '@' . idn_to_ascii($parts[1]);
|
||||||
|
} else {
|
||||||
|
$uri = idn_to_ascii($uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch the scheme of an url between http and https
|
* Switch the scheme of an url between http and https
|
||||||
|
|
|
@ -485,9 +485,8 @@ class Strings
|
||||||
* @param string $regex
|
* @param string $regex
|
||||||
* @param callable $callback
|
* @param callable $callback
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public static function performWithEscapedBlocks(string $text, string $regex, callable $callback)
|
public static function performWithEscapedBlocks(string $text, string $regex, callable $callback): string
|
||||||
{
|
{
|
||||||
// Enables nested use
|
// Enables nested use
|
||||||
$executionId = random_int(PHP_INT_MAX / 10, PHP_INT_MAX);
|
$executionId = random_int(PHP_INT_MAX / 10, PHP_INT_MAX);
|
||||||
|
|
|
@ -460,7 +460,7 @@ class XML
|
||||||
|
|
||||||
public static function getFirstNodeValue(DOMXPath $xpath, $element, $context = null)
|
public static function getFirstNodeValue(DOMXPath $xpath, $element, $context = null)
|
||||||
{
|
{
|
||||||
$result = $xpath->evaluate($element, $context);
|
$result = @$xpath->evaluate($element, $context);
|
||||||
if (!is_object($result)) {
|
if (!is_object($result)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,8 @@ namespace Friendica\Worker;
|
||||||
|
|
||||||
use Friendica\Core\Logger;
|
use Friendica\Core\Logger;
|
||||||
use Friendica\Core\Worker;
|
use Friendica\Core\Worker;
|
||||||
use Friendica\Model\Contact;
|
|
||||||
use Friendica\Model\GServer;
|
|
||||||
use Friendica\Model\Post;
|
use Friendica\Model\Post;
|
||||||
use Friendica\Protocol\ActivityPub;
|
use Friendica\Protocol\ActivityPub;
|
||||||
use Friendica\Util\HTTPSignature;
|
|
||||||
|
|
||||||
class APDelivery
|
class APDelivery
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -46,65 +42,44 @@ class APDelivery
|
||||||
public static function execute(string $cmd, int $item_id, string $inbox, int $uid, array $receivers = [], int $uri_id = 0)
|
public static function execute(string $cmd, int $item_id, string $inbox, int $uid, array $receivers = [], int $uri_id = 0)
|
||||||
{
|
{
|
||||||
if (ActivityPub\Transmitter::archivedInbox($inbox)) {
|
if (ActivityPub\Transmitter::archivedInbox($inbox)) {
|
||||||
Logger::info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uid' => $uid]);
|
Logger::info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
||||||
if (in_array($cmd, [Delivery::POST])) {
|
if (empty($uri_id) && !empty($item_id)) {
|
||||||
$item = Post::selectFirst(['uri-id'], ['id' => $item_id]);
|
$item = Post::selectFirst(['uri-id'], ['id' => $item_id]);
|
||||||
Post\DeliveryData::incrementQueueFailed($item['uri-id'] ?? 0);
|
$uri_id = $item['uri-id'] ?? 0;
|
||||||
|
}
|
||||||
|
if (empty($uri_id)) {
|
||||||
|
$posts = Post\Delivery::selectForInbox($inbox);
|
||||||
|
$uri_ids = array_column($posts, 'uri-id');
|
||||||
|
} else {
|
||||||
|
$uri_ids = [$uri_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($uri_ids as $uri_id) {
|
||||||
|
Post\Delivery::remove($uri_id, $inbox);
|
||||||
|
Post\DeliveryData::incrementQueueFailed($uri_id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::info('Invoked', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
Logger::debug('Invoked', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
||||||
|
|
||||||
$success = true;
|
if (empty($uri_id)) {
|
||||||
|
$result = ActivityPub\Delivery::deliver($inbox);
|
||||||
if ($cmd == Delivery::MAIL) {
|
$success = $result['success'];
|
||||||
$data = ActivityPub\Transmitter::createActivityFromMail($item_id);
|
$drop = false;
|
||||||
if (!empty($data)) {
|
$uri_ids = $result['uri_ids'];
|
||||||
$success = HTTPSignature::transmit($data, $inbox, $uid);
|
|
||||||
}
|
|
||||||
} elseif ($cmd == Delivery::SUGGESTION) {
|
|
||||||
$success = ActivityPub\Transmitter::sendContactSuggestion($uid, $inbox, $item_id);
|
|
||||||
} elseif ($cmd == Delivery::RELOCATION) {
|
|
||||||
// @todo Implementation pending
|
|
||||||
} elseif ($cmd == Delivery::POKE) {
|
|
||||||
// Implementation not planned
|
|
||||||
} elseif ($cmd == Delivery::REMOVAL) {
|
|
||||||
$success = ActivityPub\Transmitter::sendProfileDeletion($uid, $inbox);
|
|
||||||
} elseif ($cmd == Delivery::PROFILEUPDATE) {
|
|
||||||
$success = ActivityPub\Transmitter::sendProfileUpdate($uid, $inbox);
|
|
||||||
} else {
|
} else {
|
||||||
$data = ActivityPub\Transmitter::createCachedActivityFromItem($item_id);
|
$result = ActivityPub\Delivery::deliverToInbox($cmd, $item_id, $inbox, $uid, $receivers, $uri_id);
|
||||||
if (!empty($data)) {
|
$success = $result['success'];
|
||||||
$success = HTTPSignature::transmit($data, $inbox, $uid);
|
$drop = $result['drop'];
|
||||||
}
|
$uri_ids = [$uri_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
$gsid = null;
|
if (!$drop && !$success && !Worker::defer() && !empty($uri_ids)) {
|
||||||
|
foreach ($uri_ids as $uri_id) {
|
||||||
foreach ($receivers as $receiver) {
|
Post\Delivery::remove($uri_id, $inbox);
|
||||||
$contact = Contact::getById($receiver);
|
|
||||||
if (empty($contact)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$gsid = $gsid ?: $contact['gsid'];
|
|
||||||
|
|
||||||
if ($success) {
|
|
||||||
Contact::unmarkForArchival($contact);
|
|
||||||
} else {
|
|
||||||
Contact::markForArchival($contact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($gsid)) {
|
|
||||||
GServer::setProtocol($gsid, Post\DeliveryData::ACTIVITYPUB);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$success && !Worker::defer() && in_array($cmd, [Delivery::POST])) {
|
|
||||||
Post\DeliveryData::incrementQueueFailed($uri_id);
|
Post\DeliveryData::incrementQueueFailed($uri_id);
|
||||||
} elseif ($success && in_array($cmd, [Delivery::POST])) {
|
}
|
||||||
Post\DeliveryData::incrementQueueDone($uri_id, Post\DeliveryData::ACTIVITYPUB);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
|
|
||||||
namespace Friendica\Worker;
|
namespace Friendica\Worker;
|
||||||
|
|
||||||
use Friendica\Core\Logger;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Contact;
|
use Friendica\Model\Contact;
|
||||||
|
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||||
|
use Friendica\Network\HTTPException\NotFoundException;
|
||||||
|
|
||||||
class AddContact
|
class AddContact
|
||||||
{
|
{
|
||||||
|
@ -33,14 +35,22 @@ class AddContact
|
||||||
*/
|
*/
|
||||||
public static function execute(int $uid, string $url)
|
public static function execute(int $uid, string $url)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
if ($uid == 0) {
|
if ($uid == 0) {
|
||||||
// Adding public contact
|
// Adding public contact
|
||||||
$result = Contact::getIdForURL($url);
|
$result = Contact::getIdForURL($url);
|
||||||
Logger::info('Added public contact', ['url' => $url, 'result' => $result]);
|
DI::logger()->info('Added public contact', ['url' => $url, 'result' => $result]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = Contact::createFromProbeForUser($uid, $url);
|
$result = Contact::createFromProbeForUser($uid, $url);
|
||||||
Logger::info('Added contact', ['uid' => $uid, 'url' => $url, 'result' => $result]);
|
DI::logger()->info('Added contact for user', ['uid' => $uid, 'url' => $url, 'result' => $result]);
|
||||||
|
} catch (InternalServerErrorException $e) {
|
||||||
|
DI::logger()->warning('Internal server error.', ['exception' => $e, 'uid' => $uid, 'url' => $url]);
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
DI::logger()->notice('uid not found.', ['exception' => $e, 'uid' => $uid, 'url' => $url]);
|
||||||
|
} catch (\ImagickException $e) {
|
||||||
|
DI::logger()->notice('Imagick not found.', ['exception' => $e, 'uid' => $uid, 'url' => $url]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ use Friendica\Core\Worker;
|
||||||
use Friendica\Database\DBA;
|
use Friendica\Database\DBA;
|
||||||
use Friendica\DI;
|
use Friendica\DI;
|
||||||
use Friendica\Model\Tag;
|
use Friendica\Model\Tag;
|
||||||
|
use Friendica\Protocol\Relay;
|
||||||
|
|
||||||
class Cron
|
class Cron
|
||||||
{
|
{
|
||||||
|
@ -92,9 +93,6 @@ class Cron
|
||||||
Worker::add(PRIORITY_LOW, 'PullDirectory');
|
Worker::add(PRIORITY_LOW, 'PullDirectory');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all done workerqueue entries
|
|
||||||
Worker::add(PRIORITY_LOW, 'CleanWorkerQueue');
|
|
||||||
|
|
||||||
// Clear cache entries
|
// Clear cache entries
|
||||||
Worker::add(PRIORITY_LOW, 'ClearCache');
|
Worker::add(PRIORITY_LOW, 'ClearCache');
|
||||||
|
|
||||||
|
@ -128,6 +126,9 @@ class Cron
|
||||||
}
|
}
|
||||||
|
|
||||||
DI::config()->set('system', 'last_cron_daily', time());
|
DI::config()->set('system', 'last_cron_daily', time());
|
||||||
|
|
||||||
|
// Resubscribe to relay servers
|
||||||
|
Relay::reSubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::notice('end');
|
Logger::notice('end');
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user