Compare commits
No commits in common. "2022.05-rc" and "develop" have entirely different histories.
2022.05-rc
...
develop
|
@ -86,7 +86,3 @@ venv/
|
|||
|
||||
#Ignore cache file
|
||||
.php_cs.cache
|
||||
|
||||
#ignore avatar picture cache path
|
||||
/avatar
|
||||
|
||||
|
|
|
@ -28,11 +28,9 @@ Andy
|
|||
Andy Hee
|
||||
Angristan
|
||||
Anthronaut
|
||||
Anton
|
||||
Antron Samurai
|
||||
Arian - Cazare Muncitori
|
||||
Asher Pen
|
||||
atjn
|
||||
aweiher
|
||||
axelt
|
||||
balderino
|
||||
|
@ -57,7 +55,6 @@ Carlos Solís
|
|||
Carsten Pfeiffer
|
||||
Casper
|
||||
Cat Gray
|
||||
chinnux
|
||||
Chris Case
|
||||
Christian González
|
||||
Christian Kalkhoff
|
||||
|
@ -93,7 +90,6 @@ effex7
|
|||
Elena
|
||||
emilia.krawczyk
|
||||
Eric Côté
|
||||
Erich
|
||||
erik
|
||||
Erkan Yilmaz
|
||||
Eugene Veresk
|
||||
|
@ -117,7 +113,6 @@ Gidi Kroon
|
|||
GLComo
|
||||
greeneyedred
|
||||
Gregory Smith
|
||||
gudzpoz
|
||||
guzzisti
|
||||
Haakon Meland Eriksen
|
||||
Hans Meine
|
||||
|
@ -143,7 +138,6 @@ Joe Doe
|
|||
joe slam
|
||||
Johannes Schwab
|
||||
John Brazil
|
||||
John Mortensen
|
||||
Jonatan Nyberg
|
||||
Jonny Tischbein
|
||||
Josef Moravek
|
||||
|
@ -222,7 +216,6 @@ Philipp Holzer
|
|||
Pierre Bernardeau
|
||||
Pierre Rudloff
|
||||
Piotr Blonkowski
|
||||
Piotr Strębski
|
||||
pokerazor
|
||||
R C
|
||||
Rabuzarus
|
||||
|
@ -291,7 +284,6 @@ Tobias Diekershoff
|
|||
Tobias Hößl
|
||||
Tom
|
||||
Tom Aurlund
|
||||
Tom Hu
|
||||
tomamplius
|
||||
tomtom84
|
||||
Tony Baldwin
|
||||
|
|
|
@ -18,13 +18,11 @@
|
|||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Run the worker from a daemon.
|
||||
*
|
||||
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php
|
||||
*/
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
|
||||
exit();
|
||||
|
@ -232,7 +230,7 @@ while (true) {
|
|||
}
|
||||
|
||||
$timeout = ($seconds >= $wait_interval);
|
||||
} while (!$timeout && !Worker\IPC::JobsExists());
|
||||
} while (!$timeout && !Worker::IPCJobsExists());
|
||||
|
||||
if ($timeout) {
|
||||
$do_cron = true;
|
||||
|
|
|
@ -58,7 +58,7 @@ case "$MODE" in
|
|||
OUTFILE="$FULLPATH/../view/lang/C/messages.po"
|
||||
FINDSTARTDIR="."
|
||||
# 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 -or -path ./local -or -path ./avatar -or -path ./proxy ) -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 ) -prune -or"
|
||||
|
||||
F9KVERSION=$(cat ./VERSION);
|
||||
echo "Friendica version $F9KVERSION"
|
||||
|
|
2
boot.php
2
boot.php
|
@ -31,7 +31,7 @@ use Friendica\Model\Contact;
|
|||
|
||||
define('FRIENDICA_PLATFORM', 'Friendica');
|
||||
define('FRIENDICA_CODENAME', 'Siberian Iris');
|
||||
define('FRIENDICA_VERSION', '2022.05-rc');
|
||||
define('FRIENDICA_VERSION', '2022.05-dev');
|
||||
define('DFRN_PROTOCOL_VERSION', '2.23');
|
||||
define('NEW_TABLE_STRUCTURE_VERSION', 1288);
|
||||
|
||||
|
|
92
database.sql
92
database.sql
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2022.05-rc (Siberian Iris)
|
||||
-- DB_UPDATE_VERSION 1464
|
||||
-- Friendica 2022.05-dev (Siberian Iris)
|
||||
-- DB_UPDATE_VERSION 1460
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
@ -712,16 +712,13 @@ CREATE TABLE IF NOT EXISTS `hook` (
|
|||
--
|
||||
CREATE TABLE IF NOT EXISTS `inbox-status` (
|
||||
`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',
|
||||
`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',
|
||||
`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?',
|
||||
`shared` boolean NOT NULL DEFAULT '0' COMMENT 'Is it a shared inbox?',
|
||||
PRIMARY KEY(`url`),
|
||||
INDEX `uri-id` (`uri-id`),
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
PRIMARY KEY(`url`)
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Status of ActivityPub inboxes';
|
||||
|
||||
--
|
||||
|
@ -1071,8 +1068,8 @@ CREATE TABLE IF NOT EXISTS `post-category` (
|
|||
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`tid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`),
|
||||
INDEX `tid` (`tid`),
|
||||
INDEX `uid_uri-id` (`uid`,`uri-id`),
|
||||
INDEX `uri-id` (`tid`),
|
||||
INDEX `uid` (`uid`),
|
||||
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 (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
|
||||
|
@ -1117,25 +1114,6 @@ CREATE TABLE IF NOT EXISTS `post-content` (
|
|||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) 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
|
||||
--
|
||||
|
@ -1155,32 +1133,6 @@ CREATE TABLE IF NOT EXISTS `post-delivery-data` (
|
|||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) 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
|
||||
--
|
||||
|
@ -1219,7 +1171,6 @@ CREATE TABLE IF NOT EXISTS `post-media` (
|
|||
`publisher-image` varbinary(255) COMMENT 'Image of the publisher of the media',
|
||||
PRIMARY KEY(`id`),
|
||||
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
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Attached media';
|
||||
|
||||
|
@ -1768,9 +1719,6 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
`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`,
|
||||
`owner`.`url` AS `owner-link`,
|
||||
`owner`.`addr` AS `owner-addr`,
|
||||
|
@ -1780,7 +1728,6 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-user`.`causer-id` AS `causer-id`,
|
||||
`causer`.`url` AS `causer-link`,
|
||||
|
@ -1816,8 +1763,6 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`post-question`.`multiple` AS `question-multiple`,
|
||||
`post-question`.`voters` AS `question-voters`,
|
||||
`post-question`.`end-time` AS `question-end-time`,
|
||||
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-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`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
|
@ -1939,9 +1884,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
`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`,
|
||||
`owner`.`url` AS `owner-link`,
|
||||
`owner`.`addr` AS `owner-addr`,
|
||||
|
@ -1951,7 +1893,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-thread-user`.`causer-id` AS `causer-id`,
|
||||
`causer`.`url` AS `causer-link`,
|
||||
|
@ -1987,8 +1928,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`post-question`.`multiple` AS `question-multiple`,
|
||||
`post-question`.`voters` AS `question-voters`,
|
||||
`post-question`.`end-time` AS `question-end-time`,
|
||||
EXISTS(SELECT `uri-id` FROM `post-category` WHERE `post-category`.`uri-id` = `post-thread-user`.`uri-id` AND `post-category`.`uid` = `post-thread-user`.`uid`) AS `has-categories`,
|
||||
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread-user`.`uri-id`) AS `has-media`,
|
||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
|
@ -2096,9 +2035,6 @@ CREATE VIEW `post-view` AS SELECT
|
|||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
`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`,
|
||||
`owner`.`url` AS `owner-link`,
|
||||
`owner`.`addr` AS `owner-addr`,
|
||||
|
@ -2108,7 +2044,6 @@ CREATE VIEW `post-view` AS SELECT
|
|||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post`.`causer-id` AS `causer-id`,
|
||||
`causer`.`url` AS `causer-link`,
|
||||
|
@ -2124,8 +2059,6 @@ CREATE VIEW `post-view` AS SELECT
|
|||
`post-question`.`multiple` AS `question-multiple`,
|
||||
`post-question`.`voters` AS `question-voters`,
|
||||
`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`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
|
@ -2229,9 +2162,6 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
`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`,
|
||||
`owner`.`url` AS `owner-link`,
|
||||
`owner`.`addr` AS `owner-addr`,
|
||||
|
@ -2241,7 +2171,6 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-thread`.`causer-id` AS `causer-id`,
|
||||
`causer`.`url` AS `causer-link`,
|
||||
|
@ -2257,8 +2186,6 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`post-question`.`multiple` AS `question-multiple`,
|
||||
`post-question`.`voters` AS `question-voters`,
|
||||
`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`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
|
@ -2307,14 +2234,9 @@ CREATE VIEW `collection-view` AS SELECT
|
|||
`post-collection`.`type` AS `type`,
|
||||
`post`.`author-id` AS `cid`,
|
||||
`post`.`received` AS `received`,
|
||||
`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`
|
||||
`post`.`created` AS `created`
|
||||
FROM `post-collection`
|
||||
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`
|
||||
INNER JOIN `post-thread` ON `post-thread`.`uri-id` = `post`.`parent-uri-id`;
|
||||
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`;
|
||||
|
||||
--
|
||||
-- VIEW tag-view
|
||||
|
|
|
@ -50,9 +50,7 @@ Database Tables
|
|||
| [post-category](help/database/db_post-category) | post relation to categories |
|
||||
| [post-collection](help/database/db_post-collection) | Collection of 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-history](help/database/db_post-history) | Post history |
|
||||
| [post-link](help/database/db_post-link) | Post related external links |
|
||||
| [post-media](help/database/db_post-media) | Attached media |
|
||||
| [post-question](help/database/db_post-question) | Question |
|
||||
|
|
|
@ -9,7 +9,6 @@ Fields
|
|||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| -------- | ------------------------------------ | -------------- | ---- | --- | ------------------- | ----- |
|
||||
| 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 | |
|
||||
| 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 | |
|
||||
|
@ -23,13 +22,6 @@ Indexes
|
|||
| Name | Fields |
|
||||
| ------- | ------ |
|
||||
| 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)
|
||||
|
|
|
@ -16,11 +16,11 @@ Fields
|
|||
Indexes
|
||||
------------
|
||||
|
||||
| Name | Fields |
|
||||
| ---------- | ---------------------- |
|
||||
| PRIMARY | uri-id, uid, type, tid |
|
||||
| tid | tid |
|
||||
| uid_uri-id | uid, uri-id |
|
||||
| Name | Fields |
|
||||
| ------- | ---------------------- |
|
||||
| PRIMARY | uri-id, uid, type, tid |
|
||||
| uri-id | tid |
|
||||
| uid | uid |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
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)
|
|
@ -1,44 +0,0 @@
|
|||
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,7 +35,6 @@ Indexes
|
|||
| ---------- | ------------------- |
|
||||
| PRIMARY | id |
|
||||
| uri-id-url | UNIQUE, uri-id, url |
|
||||
| uri-id-id | uri-id, id |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
|
|
@ -110,13 +110,7 @@ function display_init(App $a)
|
|||
$item = $parent ?: $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);
|
||||
}
|
||||
DI::page()['aside'] = Widget\VCard::getHTML(display_fetchauthor($item));
|
||||
}
|
||||
|
||||
function display_fetchauthor($item)
|
||||
|
|
|
@ -39,11 +39,11 @@ use Friendica\Util\Strings;
|
|||
function fbrowser_content(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if (DI::args()->getArgc() == 1) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// 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,11 +621,6 @@ function item_post(App $a) {
|
|||
$datarray["id"] = -1;
|
||||
$datarray["uri-id"] = -1;
|
||||
$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);
|
||||
|
||||
|
@ -668,17 +663,21 @@ function item_post(App $a) {
|
|||
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
|
||||
|
||||
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 = [
|
||||
'title' => $datarray['title'],
|
||||
'body' => $datarray['body'],
|
||||
'attach' => $datarray['attach'],
|
||||
'file' => $datarray['file'],
|
||||
'rendered-html' => $datarray['rendered-html'],
|
||||
'rendered-hash' => $datarray['rendered-hash'],
|
||||
'edited' => DateTimeFormat::utcNow(),
|
||||
'changed' => DateTimeFormat::utcNow()
|
||||
];
|
||||
'changed' => DateTimeFormat::utcNow()];
|
||||
|
||||
Item::update($fields, ['id' => $post_id]);
|
||||
Item::updateDisplayCache($datarray['uri-id']);
|
||||
|
||||
if ($return_path) {
|
||||
DI::baseUrl()->redirect($return_path);
|
||||
|
|
|
@ -65,7 +65,7 @@ function photos_init(App $a) {
|
|||
|
||||
if (DI::args()->getArgc() > 1) {
|
||||
$owner = User::getOwnerDataByNick(DI::args()->getArgv()[1]);
|
||||
if (empty($owner) || $owner['account_removed']) {
|
||||
if (!$owner) {
|
||||
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ function photos_post(App $a)
|
|||
|
||||
if (!$can_post) {
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$owner_record = User::getOwnerDataById($page_owner_uid);
|
||||
|
@ -166,7 +166,7 @@ function photos_post(App $a)
|
|||
if (!$owner_record) {
|
||||
notice(DI::l10n()->t('Contact information unavailable'));
|
||||
DI::logger()->info('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
|
@ -1257,12 +1257,14 @@ function photos_content(App $a)
|
|||
$tags = null;
|
||||
|
||||
if (!empty($link_item['id'])) {
|
||||
$tag_text = Tag::getCSVByURIId($link_item['uri-id']);
|
||||
$arr = explode(',', $tag_text);
|
||||
// parse tags and add links
|
||||
$tag_arr = [];
|
||||
foreach (Tag::getByURIId($link_item['uri-id']) as $tag) {
|
||||
foreach ($arr as $tag) {
|
||||
$tag_arr[] = [
|
||||
'name' => $tag['name'],
|
||||
'removeurl' => '/tagrm/' . $link_item['id'] . '/' . bin2hex($tag['name'])
|
||||
'name' => BBCode::convert($tag),
|
||||
'removeurl' => '/tagrm/' . $link_item['id'] . '/' . bin2hex($tag)
|
||||
];
|
||||
}
|
||||
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
use Friendica\App;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
|
@ -39,7 +38,7 @@ function hub_return($valid, $body)
|
|||
} else {
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// when receiving an XML feed, always return OK
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\PushSubscriber;
|
||||
|
@ -143,5 +142,5 @@ function pubsubhubbub_init(App $a) {
|
|||
|
||||
throw new \Friendica\Network\HTTPException\AcceptedException();
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
|
|
@ -23,16 +23,25 @@ use Friendica\App;
|
|||
use Friendica\BaseModule;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Content\Nav;
|
||||
use Friendica\Core\ACL;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Notification;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Module\BaseSettings;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Email;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Worker\Delivery;
|
||||
|
||||
function settings_init(App $a)
|
||||
{
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
|
@ -31,7 +30,7 @@ function share_init(App $a) {
|
|||
$post_id = ((DI::args()->getArgc() > 1) ? intval(DI::args()->getArgv()[1]) : 0);
|
||||
|
||||
if (!$post_id || !local_user()) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$fields = ['private', 'body', 'author-name', 'author-link', 'author-avatar',
|
||||
|
@ -39,7 +38,7 @@ function share_init(App $a) {
|
|||
$item = Post::selectFirst($fields, ['id' => $post_id]);
|
||||
|
||||
if (!DBA::isResult($item) || $item['private'] == Item::PRIVATE) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if (strpos($item['body'], "[/share]") !== false) {
|
||||
|
@ -57,5 +56,5 @@ function share_init(App $a) {
|
|||
}
|
||||
|
||||
echo $o;
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
|
|
@ -167,5 +167,6 @@ EOT;
|
|||
$post = Post::selectFirst(['uri-id', 'uid'], ['id' => $post_id]);
|
||||
|
||||
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.')]);
|
||||
}
|
||||
notice(DI::l10n()->t('Permission denied.') . EOL );
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($_FILES['userfile'])) {
|
||||
if ($r_json) {
|
||||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$src = $_FILES['userfile']['tmp_name'];
|
||||
|
@ -97,7 +97,7 @@ function wall_attach_post(App $a) {
|
|||
} else {
|
||||
notice($msg);
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($maxfilesize && $filesize > $maxfilesize) {
|
||||
|
@ -108,7 +108,7 @@ function wall_attach_post(App $a) {
|
|||
} else {
|
||||
echo $msg . EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$newid = Attach::storeFile($src, $page_owner_uid, $filename, '<' . $page_owner_cid . '>');
|
||||
|
@ -122,7 +122,7 @@ function wall_attach_post(App $a) {
|
|||
} else {
|
||||
echo $msg . EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($r_json) {
|
||||
|
@ -132,6 +132,7 @@ function wall_attach_post(App $a) {
|
|||
$lf = "\n";
|
||||
|
||||
echo $lf . $lf . '[attachment]' . $newid . '[/attachment]' . $lf;
|
||||
System::exit();
|
||||
|
||||
exit();
|
||||
// NOTREACHED
|
||||
}
|
||||
|
|
|
@ -89,14 +89,14 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
System::jsonExit(['error' => DI::l10n()->t('Permission denied.')]);
|
||||
}
|
||||
notice(DI::l10n()->t('Permission denied.'));
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if (empty($_FILES['userfile']) && empty($_FILES['media'])) {
|
||||
if ($r_json) {
|
||||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$src = '';
|
||||
|
@ -148,7 +148,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
System::jsonExit(['error' => DI::l10n()->t('Invalid request.')]);
|
||||
}
|
||||
notice(DI::l10n()->t('Invalid request.'));
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
||||
|
@ -167,7 +167,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
} else {
|
||||
echo $msg. EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$Image->orient($src);
|
||||
|
@ -205,7 +205,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
} else {
|
||||
echo $msg. EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ function wall_upload_post(App $a, $desktopmode = true)
|
|||
} else {
|
||||
echo $msg. EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
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";
|
||||
System::exit();
|
||||
exit();
|
||||
// NOTREACHED
|
||||
}
|
||||
|
|
|
@ -472,7 +472,7 @@ class App
|
|||
// 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
|
||||
$user = $this->database->selectFirst('user', ['theme'], ['uid' => $this->profile_owner]);
|
||||
if ($this->database->isResult($user) && !local_user()) {
|
||||
if ($this->database->isResult($user) && !$this->pConfig->get(local_user(), 'system', 'always_my_theme')) {
|
||||
$page_theme = $user['theme'];
|
||||
}
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ class App
|
|||
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.
|
||||
// This works only if the user is on the same server
|
||||
if (!local_user()) {
|
||||
if (!$this->pConfig->get(local_user(), 'system', 'always_my_theme')) {
|
||||
$page_mobile_theme = $this->pConfig->get($this->profile_owner, 'system', 'mobile-theme');
|
||||
}
|
||||
}
|
||||
|
@ -576,7 +576,6 @@ class App
|
|||
$this->profiler->set(microtime(true), 'classinit');
|
||||
|
||||
$moduleName = $this->args->getModuleName();
|
||||
$page->setLogging($this->args->getCommand(), $this->args->getMethod());
|
||||
|
||||
try {
|
||||
// Missing DB connection: ERROR
|
||||
|
@ -719,7 +718,6 @@ class App
|
|||
} catch (HTTPException $e) {
|
||||
(new ModuleHTTPException())->rawContent($e);
|
||||
}
|
||||
$page->logRuntime($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,7 +30,6 @@ use Friendica\Core\Config\Capability\IManageConfigValues;
|
|||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Theme;
|
||||
|
@ -79,37 +78,14 @@ class Page implements ArrayAccess
|
|||
*/
|
||||
private $basePath;
|
||||
|
||||
private $timestamp = 0;
|
||||
private $command = '';
|
||||
private $method = '';
|
||||
|
||||
/**
|
||||
* @param string $basepath The Page basepath
|
||||
*/
|
||||
public function __construct(string $basepath)
|
||||
{
|
||||
$this->timestamp = microtime(true);
|
||||
$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
|
||||
*
|
||||
|
@ -445,9 +421,6 @@ class Page implements ArrayAccess
|
|||
{
|
||||
$moduleName = $args->getModuleName();
|
||||
|
||||
$this->command = $moduleName;
|
||||
$this->method = $args->getMethod();
|
||||
|
||||
/* Create the page content.
|
||||
* Calls all hooks which are including content operations
|
||||
*
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
<?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']);
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
<?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
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function networkToIcon($network, $profile = "", $gsid = 0)
|
||||
public static function networkToIcon($network, $profile = "")
|
||||
{
|
||||
$nets = [
|
||||
Protocol::DFRN => 'friendica',
|
||||
|
@ -218,14 +218,7 @@ class ContactSelector
|
|||
$network_icon = str_replace($search, $replace, $network);
|
||||
|
||||
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'])) {
|
||||
$network_icon = $platform_icons[strtolower($gserver['platform'])] ?? $network_icon;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ use Friendica\Protocol\Activity;
|
|||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -107,8 +108,6 @@ class Conversation
|
|||
*/
|
||||
public function builtinActivityPuller(array $activity, array &$conv_responses)
|
||||
{
|
||||
$thread_parent = $activity['thr-parent-row'] ?? [];
|
||||
|
||||
foreach ($conv_responses as $mode => $v) {
|
||||
$sparkle = '';
|
||||
|
||||
|
@ -153,8 +152,9 @@ class Conversation
|
|||
$activity['thr-parent-id'] = $activity['parent-uri-id'];
|
||||
}
|
||||
|
||||
// Skip when the causer of the parent is the same as the author of the announce
|
||||
if (($verb == Activity::ANNOUNCE) && !empty($thread_parent['causer-id'] && ($thread_parent['causer-id'] == $activity['author-id']))) {
|
||||
// Skip when the causer of the parent is the same than the author of the announce
|
||||
if (($verb == Activity::ANNOUNCE) && Post::exists(['uri-id' => $activity['thr-parent-id'],
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -441,7 +441,7 @@ class Conversation
|
|||
$previewing = (($preview) ? ' preview ' : '');
|
||||
|
||||
if ($mode === 'network') {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
$items = $this->addChildren($items, false, $order, $uid);
|
||||
if (!$update) {
|
||||
/*
|
||||
* The special div is needed for liveUpdate to kick in for this page.
|
||||
|
@ -467,7 +467,7 @@ class Conversation
|
|||
. "'; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === 'profile') {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
$items = $this->addChildren($items, false, $order, $uid);
|
||||
|
||||
if (!$update) {
|
||||
$tab = !empty($_GET['tab']) ? trim($_GET['tab']) : 'posts';
|
||||
|
@ -484,7 +484,7 @@ class Conversation
|
|||
}
|
||||
}
|
||||
} elseif ($mode === 'notes') {
|
||||
$items = $this->addChildren($items, false, $order, local_user(), $mode);
|
||||
$items = $this->addChildren($items, false, $order, local_user());
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-notes"></div>' . "\r\n"
|
||||
|
@ -492,7 +492,7 @@ class Conversation
|
|||
. "; var netargs = '/?f='; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === 'display') {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
$items = $this->addChildren($items, false, $order, $uid);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-display"></div>' . "\r\n"
|
||||
|
@ -500,7 +500,7 @@ class Conversation
|
|||
. "</script>";
|
||||
}
|
||||
} elseif ($mode === 'community') {
|
||||
$items = $this->addChildren($items, true, $order, $uid, $mode);
|
||||
$items = $this->addChildren($items, true, $order, $uid);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-community"></div>' . "\r\n"
|
||||
|
@ -510,7 +510,7 @@ class Conversation
|
|||
. "'; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === 'contacts') {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
$items = $this->addChildren($items, false, $order, $uid);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-contact"></div>' . "\r\n"
|
||||
|
@ -667,15 +667,15 @@ class Conversation
|
|||
'created_date' => $item['created'],
|
||||
'uriid' => $item['uri-id'],
|
||||
'network' => $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'], $item['author-gsid']),
|
||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network']),
|
||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
|
||||
'linktitle' => $this->l10n->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
||||
'profile_url' => $profile_link,
|
||||
'item_photo_menu_html' => $this->item->photoMenu($item, $formSecurityToken),
|
||||
'name' => $profile_name,
|
||||
'sparkle' => $sparkle,
|
||||
'lock' => false,
|
||||
'thumb' => $this->baseURL->remove($this->item->getAuthorAvatar($item)),
|
||||
'thumb' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['author-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
||||
'title' => $title,
|
||||
'body_html' => $body_html,
|
||||
'tags' => $tags['tags'],
|
||||
|
@ -696,7 +696,7 @@ class Conversation
|
|||
'indent' => '',
|
||||
'owner_name' => '',
|
||||
'owner_url' => '',
|
||||
'owner_photo' => $this->baseURL->remove($this->item->getOwnerAvatar($item)),
|
||||
'owner_photo' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
||||
'plink' => ItemModel::getPlink($item),
|
||||
'edpost' => false,
|
||||
'pinned' => $pinned,
|
||||
|
@ -809,13 +809,12 @@ class Conversation
|
|||
/**
|
||||
* Adds some information (Causer, post reason, direction) to the fetched post row.
|
||||
*
|
||||
* @param array $row Post row
|
||||
* @param array $activity Contact data of the resharer
|
||||
* @param array $thr_parent Thread parent row
|
||||
* @param array $row Post row
|
||||
* @param array $activity Contact data of the resharer
|
||||
*
|
||||
* @return array items with parents and comments
|
||||
*/
|
||||
private function addRowInformation(array $row, array $activity, array $thr_parent)
|
||||
private function addRowInformation(array $row, array $activity)
|
||||
{
|
||||
$this->profiler->startRecording('rendering');
|
||||
|
||||
|
@ -890,8 +889,6 @@ class Conversation
|
|||
break;
|
||||
}
|
||||
|
||||
$row['thr-parent-row'] = $thr_parent;
|
||||
|
||||
$this->profiler->stopRecording();
|
||||
return $row;
|
||||
}
|
||||
|
@ -902,15 +899,15 @@ class Conversation
|
|||
* The system will fetch the comments for the local user whenever possible.
|
||||
* This behaviour is currently needed to allow commenting on Friendica posts.
|
||||
*
|
||||
* @param array $parents Parent items
|
||||
* @param bool $block_authors
|
||||
* @param bool $order
|
||||
* @param int $uid
|
||||
* @param string $mode
|
||||
* @param array $parents Parent items
|
||||
*
|
||||
* @param $block_authors
|
||||
* @param $order
|
||||
* @param $uid
|
||||
* @return array items with parents and comments
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode)
|
||||
private function addChildren(array $parents, $block_authors, $order, $uid)
|
||||
{
|
||||
$this->profiler->startRecording('rendering');
|
||||
if (count($parents) > 1) {
|
||||
|
@ -919,6 +916,8 @@ class Conversation
|
|||
$max_comments = $this->config->get('system', 'max_display_comments', 1000);
|
||||
}
|
||||
|
||||
$params = ['order' => ['uri-id' => true, 'uid' => true]];
|
||||
|
||||
$activities = [];
|
||||
$uriids = [];
|
||||
$commentcounter = [];
|
||||
|
@ -952,17 +951,6 @@ class Conversation
|
|||
$condition = DBA::mergeConditions($condition,
|
||||
["`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);
|
||||
|
||||
$items = [];
|
||||
|
@ -972,10 +960,6 @@ class Conversation
|
|||
continue;
|
||||
}
|
||||
|
||||
if (($mode != 'contacts') && !$row['origin']) {
|
||||
$row['featured'] = false;
|
||||
}
|
||||
|
||||
if ($max_comments > 0) {
|
||||
if (($row['gravity'] == GRAVITY_COMMENT) && (++$commentcounter[$row['parent-uri-id']] > $max_comments)) {
|
||||
continue;
|
||||
|
@ -984,8 +968,7 @@ class Conversation
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$items[$row['uri-id']] = $this->addRowInformation($row, $activities[$row['uri-id']] ?? [], $thr_parent[$row['thr-parent-id']] ?? []);
|
||||
$items[$row['uri-id']] = $this->addRowInformation($row, $activities[$row['uri-id']] ?? []);
|
||||
}
|
||||
|
||||
DBA::close($thread_items);
|
||||
|
|
|
@ -26,16 +26,16 @@ use Friendica\Core\Hook;
|
|||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item as ModelItem;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
/**
|
||||
|
@ -93,10 +93,6 @@ class Item
|
|||
|
||||
$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) {
|
||||
if (!empty($item['author-link'])) {
|
||||
$url = $item['author-link'] . "?category=" . rawurlencode($savedFolderName);
|
||||
|
@ -357,6 +353,22 @@ 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();
|
||||
}
|
||||
|
||||
|
@ -386,7 +398,7 @@ class Item
|
|||
$pcid = $item['author-id'];
|
||||
$network = '';
|
||||
$rel = 0;
|
||||
$condition = ['uid' => local_user(), 'uri-id' => $item['author-uri-id']];
|
||||
$condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])];
|
||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
|
@ -565,43 +577,4 @@ class 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,20 +1952,17 @@ class BBCode
|
|||
* - #[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) {
|
||||
if ($simple_html == self::ACTIVITYPUB) {
|
||||
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" data-tag="' . XML::escape($matches[1]) . '" rel="tag ugc">#'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
} else {
|
||||
return '#<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
}
|
||||
}, $text);
|
||||
return $text;
|
||||
});
|
||||
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) {
|
||||
if ($simple_html == self::ACTIVITYPUB) {
|
||||
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" data-tag="' . XML::escape($matches[1]) . '" rel="tag ugc">#'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
} else {
|
||||
return '#<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
|
||||
. '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">'
|
||||
. XML::escape($matches[1]) . '</a>';
|
||||
}
|
||||
}, $text);
|
||||
|
||||
// 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
|
||||
|
@ -2089,15 +2086,11 @@ class BBCode
|
|||
* @param string $text The text with BBCode
|
||||
* @return string The same text - but without "abstract" element
|
||||
*/
|
||||
public static function stripAbstract(string $text): string
|
||||
public static function stripAbstract($text)
|
||||
{
|
||||
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);
|
||||
return $text;
|
||||
});
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
|
||||
|
||||
DI::profiler()->stopRecording();
|
||||
return $text;
|
||||
|
@ -2106,26 +2099,30 @@ class BBCode
|
|||
/**
|
||||
* Returns the value of the "abstract" element
|
||||
*
|
||||
* @param string $text The text that maybe contains the element
|
||||
* @param string $text The text that maybe contains the element
|
||||
* @param string $addon The addon for which the abstract is meant for
|
||||
* @return string The abstract
|
||||
*/
|
||||
public static function getAbstract(string $text, string $addon = ''): string
|
||||
public static function getAbstract($text, $addon = '')
|
||||
{
|
||||
DI::profiler()->startRecording('rendering');
|
||||
$abstract = '';
|
||||
$abstracts = [];
|
||||
$addon = strtolower($addon);
|
||||
|
||||
$abstract = BBCode::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) use ($addon) {
|
||||
if ($addon && preg_match('#\[abstract=' . preg_quote($addon, '#') . '](.*?)\[/abstract]#ism', $text, $matches)) {
|
||||
return $matches[1];
|
||||
if (preg_match_all("/\[abstract=(.*?)\](.*?)\[\/abstract\]/ism", $text, $results, PREG_SET_ORDER)) {
|
||||
foreach ($results as $result) {
|
||||
$abstracts[strtolower($result[1])] = $result[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match("#\[abstract](.*?)\[/abstract]#ism", $text, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
if (isset($abstracts[$addon])) {
|
||||
$abstract = $abstracts[$addon];
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
if ($abstract == '' && preg_match("/\[abstract\](.*?)\[\/abstract\]/ism", $text, $result)) {
|
||||
$abstract = $result[1];
|
||||
}
|
||||
|
||||
DI::profiler()->stopRecording();
|
||||
return $abstract;
|
||||
|
@ -2340,9 +2337,11 @@ class BBCode
|
|||
* @param array $tagList A list of tag names, e.g ['noparse', 'nobb', 'pre']
|
||||
* @param callable $callback
|
||||
* @return string
|
||||
* @see Strings::performWithEscapedBlocks
|
||||
* @throws Exception
|
||||
*@see Strings::performWithEscapedBlocks
|
||||
*
|
||||
*/
|
||||
public static function performWithEscapedTags(string $text, array $tagList, callable $callback): string
|
||||
public static function performWithEscapedTags(string $text, array $tagList, callable $callback)
|
||||
{
|
||||
$tagList = array_map('preg_quote', $tagList);
|
||||
|
||||
|
|
|
@ -59,13 +59,11 @@ Commands:
|
|||
autoinstall Starts automatic installation of friendica based on values from htconfig.php
|
||||
lock Edit site locks
|
||||
maintenance Set maintenance mode for this node
|
||||
movetoavatarcache Move cached avatars to the file based avatar cache
|
||||
user User management
|
||||
php2po Generate a messages.po file from a strings.php file
|
||||
po2php Generate a strings.php file from a messages.po file
|
||||
typo Checks for parse errors in Friendica files
|
||||
postupdate Execute pending post update scripts (can last days)
|
||||
relocate Update node base URL
|
||||
serverblock Manage blocked servers
|
||||
storage Manage storage backend
|
||||
relay Manage ActivityPub relay servers
|
||||
|
@ -93,12 +91,10 @@ HELP;
|
|||
'globalcommunitysilence' => Friendica\Console\GlobalCommunitySilence::class,
|
||||
'lock' => Friendica\Console\Lock::class,
|
||||
'maintenance' => Friendica\Console\Maintenance::class,
|
||||
'movetoavatarcache' => Friendica\Console\MoveToAvatarCache::class,
|
||||
'php2po' => Friendica\Console\PhpToPo::class,
|
||||
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||
'po2php' => Friendica\Console\PoToPhp::class,
|
||||
'relay' => Friendica\Console\Relay::class,
|
||||
'relocate' => Friendica\Console\Relocate::class,
|
||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||
'storage' => Friendica\Console\Storage::class,
|
||||
'test' => Friendica\Console\Test::class,
|
||||
|
|
|
@ -40,7 +40,6 @@ class L10n
|
|||
'bg' => 'Български',
|
||||
'ca' => 'Català',
|
||||
'cs' => 'Česky',
|
||||
'da-dk' => 'Dansk (Danmark)',
|
||||
'de' => 'Deutsch',
|
||||
'en-gb' => 'English (United Kingdom)',
|
||||
'en-us' => 'English (United States)',
|
||||
|
|
|
@ -295,7 +295,7 @@ class System
|
|||
DI::apiResponse()->addContent(XML::fromArray(["result" => $result], $xml));
|
||||
DI::page()->exit(DI::apiResponse()->generate());
|
||||
|
||||
self::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,7 +315,7 @@ class System
|
|||
DI::apiResponse()->addContent($content);
|
||||
DI::page()->exit(DI::apiResponse()->generate());
|
||||
|
||||
self::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,8 +331,7 @@ class System
|
|||
DI::apiResponse()->setType($responce, $content_type);
|
||||
DI::apiResponse()->addContent($content);
|
||||
DI::page()->exit(DI::apiResponse()->generate());
|
||||
|
||||
self::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
public static function jsonError($httpCode, $content, $content_type = 'application/json')
|
||||
|
@ -360,16 +359,6 @@ class System
|
|||
DI::apiResponse()->setType(Response::TYPE_JSON, $content_type);
|
||||
DI::apiResponse()->addContent(json_encode($content, $options));
|
||||
DI::page()->exit(DI::apiResponse()->generate());
|
||||
|
||||
self::exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit the program execution.
|
||||
*/
|
||||
public static function exit()
|
||||
{
|
||||
DI::page()->logRuntime(DI::config());
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -459,7 +448,8 @@ class System
|
|||
case 307:
|
||||
throw new TemporaryRedirectException();
|
||||
}
|
||||
self::exit();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -532,7 +522,7 @@ class System
|
|||
echo str_replace("\t", " ", $o);
|
||||
echo "</section>";
|
||||
echo "</body></html>\r\n";
|
||||
self::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core;
|
||||
use Friendica\Core\Worker\Entity\Process;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
@ -49,6 +51,7 @@ class Worker
|
|||
private static $lock_duration = 0;
|
||||
private static $last_update;
|
||||
private static $state;
|
||||
private static $daemon_mode = null;
|
||||
/** @var Process */
|
||||
private static $process;
|
||||
|
||||
|
@ -77,7 +80,7 @@ class Worker
|
|||
$last_cleanup = DI::config()->get('system', 'worker_last_cleaned', 0);
|
||||
if (time() > ($last_cleanup + 300)) {
|
||||
DI::config()->set('system', 'worker_last_cleaned', time());
|
||||
Worker\Cron::killStaleWorkers();
|
||||
self::killStaleWorkers();
|
||||
}
|
||||
|
||||
// Check if the system is ready
|
||||
|
@ -87,7 +90,7 @@ class Worker
|
|||
|
||||
// Now we start additional cron processes if we should do so
|
||||
if ($run_cron) {
|
||||
Worker\Cron::run();
|
||||
self::runCron();
|
||||
}
|
||||
|
||||
$last_check = $starttime = time();
|
||||
|
@ -95,13 +98,16 @@ class Worker
|
|||
|
||||
// We fetch the next queue entry that is about to be executed
|
||||
while ($r = self::workerProcess()) {
|
||||
if (Worker\IPC::JobsExists(getmypid())) {
|
||||
Worker\IPC::DeleteJobState(getmypid());
|
||||
if (self::IPCJobsExists(getmypid())) {
|
||||
self::IPCDeleteJobState(getmypid());
|
||||
}
|
||||
|
||||
// Don't refetch when a worker fetches tasks for multiple workers
|
||||
$refetched = DI::config()->get('system', 'worker_multiple_fetch');
|
||||
foreach ($r as $entry) {
|
||||
// Assure that the priority is an integer value
|
||||
$entry['priority'] = (int)$entry['priority'];
|
||||
|
||||
// The work will be done
|
||||
if (!self::execute($entry)) {
|
||||
Logger::notice('Process execution failed, quitting.');
|
||||
|
@ -146,8 +152,8 @@ class Worker
|
|||
if (time() > ($starttime + (DI::config()->get('system', 'cron_interval') * 60))) {
|
||||
Logger::info('Process lifetime reached, respawning.');
|
||||
self::unclaimProcess($process);
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(true);
|
||||
if (self::isDaemonMode()) {
|
||||
self::IPCSetJobState(true);
|
||||
} else {
|
||||
self::spawnWorker();
|
||||
}
|
||||
|
@ -156,8 +162,8 @@ class Worker
|
|||
}
|
||||
|
||||
// Cleaning up. Possibly not needed, but it doesn't harm anything.
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(false);
|
||||
if (self::isDaemonMode()) {
|
||||
self::IPCSetJobState(false);
|
||||
}
|
||||
Logger::info("Couldn't select a workerqueue entry, quitting process", ['pid' => getmypid()]);
|
||||
}
|
||||
|
@ -255,7 +261,7 @@ class Worker
|
|||
$workerqueue = DBA::selectFirst('workerqueue', ['priority'], $condition, ['order' => ['priority']]);
|
||||
self::$db_duration += (microtime(true) - $stamp);
|
||||
if (DBA::isResult($workerqueue)) {
|
||||
return $workerqueue['priority'];
|
||||
return $workerqueue["priority"];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -460,13 +466,13 @@ class Worker
|
|||
|
||||
$cooldown = DI::config()->get("system", "worker_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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -474,6 +480,11 @@ class Worker
|
|||
// For this reason the variables have to be initialized.
|
||||
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);
|
||||
|
||||
$up_duration = microtime(true) - self::$up_start;
|
||||
|
@ -518,21 +529,21 @@ class Worker
|
|||
self::$lock_duration = 0;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -620,6 +631,87 @@ class Worker
|
|||
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
|
||||
|
@ -686,12 +778,12 @@ class Worker
|
|||
self::$db_duration_stat += (microtime(true) - $stamp);
|
||||
while ($entry = DBA::fetch($jobs)) {
|
||||
$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_stat += (microtime(true) - $stamp);
|
||||
$idle_workers -= $running;
|
||||
$waiting_processes += $entry["entries"];
|
||||
$listitem[$entry['priority']] = $entry['priority'] . ":" . $running . "/" . $entry["entries"];
|
||||
$listitem[$entry["priority"]] = $entry["priority"] . ":" . $running . "/" . $entry["entries"];
|
||||
}
|
||||
DBA::close($jobs);
|
||||
} else {
|
||||
|
@ -703,7 +795,7 @@ class Worker
|
|||
|
||||
while ($entry = DBA::fetch($jobs)) {
|
||||
$idle_workers -= $entry["running"];
|
||||
$listitem[$entry['priority']] = $entry['priority'].":".$entry["running"];
|
||||
$listitem[$entry["priority"]] = $entry["priority"].":".$entry["running"];
|
||||
}
|
||||
DBA::close($jobs);
|
||||
}
|
||||
|
@ -729,8 +821,8 @@ class Worker
|
|||
// 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()) {
|
||||
Logger::info("There are fewer workers as possible, fork a new worker.", ['active' => $active, 'queues' => $queues]);
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(true);
|
||||
if (self::isDaemonMode()) {
|
||||
self::IPCSetJobState(true);
|
||||
} else {
|
||||
self::spawnWorker();
|
||||
}
|
||||
|
@ -738,8 +830,8 @@ class Worker
|
|||
}
|
||||
|
||||
// if there are too much worker, we don't spawn a new one.
|
||||
if (Worker\Daemon::isMode() && ($active > $queues)) {
|
||||
Worker\IPC::SetJobState(false);
|
||||
if (self::isDaemonMode() && ($active > $queues)) {
|
||||
self::IPCSetJobState(false);
|
||||
}
|
||||
|
||||
return $active > $queues;
|
||||
|
@ -1030,6 +1122,26 @@ class Worker
|
|||
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
|
||||
*
|
||||
|
@ -1055,11 +1167,11 @@ class Worker
|
|||
// The parent process continues here
|
||||
DBA::connect();
|
||||
|
||||
Worker\IPC::SetJobState(true, $pid);
|
||||
self::IPCSetJobState(true, $pid);
|
||||
Logger::info('Spawned new worker', ['pid' => $pid]);
|
||||
|
||||
$cycles = 0;
|
||||
while (Worker\IPC::JobsExists($pid) && (++$cycles < 100)) {
|
||||
while (self::IPCJobsExists($pid) && (++$cycles < 100)) {
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
|
@ -1074,7 +1186,7 @@ class Worker
|
|||
$process = DI::process()->create(getmypid(), basename(__FILE__));
|
||||
|
||||
$cycles = 0;
|
||||
while (!Worker\IPC::JobsExists($process->pid) && (++$cycles < 100)) {
|
||||
while (!self::IPCJobsExists($process->pid) && (++$cycles < 100)) {
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
|
@ -1084,7 +1196,7 @@ class Worker
|
|||
|
||||
self::unclaimProcess($process);
|
||||
|
||||
Worker\IPC::SetJobState(false, $process->pid);
|
||||
self::IPCSetJobState(false, $process->pid);
|
||||
DI::process()->delete($process);
|
||||
Logger::info('Worker ended', ['pid' => $process->pid]);
|
||||
exit();
|
||||
|
@ -1099,13 +1211,13 @@ class Worker
|
|||
*/
|
||||
public static function spawnWorker($do_cron = false)
|
||||
{
|
||||
if (Worker\Daemon::isMode() && DI::config()->get('system', 'worker_fork')) {
|
||||
if (self::isDaemonMode() && DI::config()->get('system', 'worker_fork')) {
|
||||
self::forkProcess($do_cron);
|
||||
} else {
|
||||
DI::system()->run('bin/worker.php', ['no_cron' => !$do_cron]);
|
||||
}
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(false);
|
||||
if (self::isDaemonMode()) {
|
||||
self::IPCSetJobState(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1175,7 +1287,7 @@ class Worker
|
|||
$found = DBA::exists('workerqueue', ['command' => $command, 'parameter' => $parameters, 'done' => false]);
|
||||
$added = 0;
|
||||
|
||||
if (!is_int($priority) || !in_array($priority, PRIORITIES)) {
|
||||
if (!in_array($priority, PRIORITIES)) {
|
||||
Logger::warning('Invalid priority', ['priority' => $priority, 'command' => $command, 'callstack' => System::callstack(20)]);
|
||||
$priority = PRIORITY_MEDIUM;
|
||||
}
|
||||
|
@ -1196,11 +1308,11 @@ class Worker
|
|||
}
|
||||
|
||||
// Set the IPC flag to ensure an immediate process execution via daemon
|
||||
if (Worker\Daemon::isMode()) {
|
||||
Worker\IPC::SetJobState(true);
|
||||
if (self::isDaemonMode()) {
|
||||
self::IPCSetJobState(true);
|
||||
}
|
||||
|
||||
Worker\Daemon::checkState();
|
||||
self::checkDaemonState();
|
||||
|
||||
// Should we quit and wait for the worker to be called as a cronjob?
|
||||
if ($dont_fork) {
|
||||
|
@ -1221,7 +1333,7 @@ class Worker
|
|||
}
|
||||
|
||||
// Quit on daemon mode
|
||||
if (Worker\Daemon::isMode()) {
|
||||
if (self::isDaemonMode()) {
|
||||
return $added;
|
||||
}
|
||||
|
||||
|
@ -1311,6 +1423,159 @@ class Worker
|
|||
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
|
||||
*
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
<?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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?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?
|
||||
*/
|
||||
public function transaction(): bool
|
||||
public function transaction()
|
||||
{
|
||||
if (!$this->performCommit()) {
|
||||
return false;
|
||||
|
@ -1790,32 +1790,4 @@ class Database
|
|||
{
|
||||
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;
|
||||
|
||||
use Friendica\Contact\Avatar;
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
|
||||
use Friendica\Content\Pager;
|
||||
use Friendica\Content\Text\HTML;
|
||||
|
@ -35,7 +35,6 @@ use Friendica\Core\Worker;
|
|||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\NoScrape;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
@ -683,7 +682,7 @@ class Contact
|
|||
*/
|
||||
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
||||
{
|
||||
$fields = ['id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
||||
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||
|
@ -715,7 +714,6 @@ class Contact
|
|||
// 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['nurl'] = Strings::normaliseLink($fields['url']);
|
||||
$fields['uri-id'] = ItemURI::getIdByURI($fields['url']);
|
||||
$fields['addr'] = $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
|
||||
$fields['request'] = DI::baseUrl() . '/dfrn_request/' . $user['nickname'];
|
||||
$fields['notify'] = DI::baseUrl() . '/dfrn_notify/' . $user['nickname'];
|
||||
|
@ -773,10 +771,10 @@ class Contact
|
|||
$fields['updated'] = DateTimeFormat::utcNow();
|
||||
self::update($fields, ['id' => $self['id']]);
|
||||
|
||||
// Update the other contacts as well
|
||||
unset($fields['prvkey']);
|
||||
$fields['self'] = false;
|
||||
self::update($fields, ['uri-id' => $self['uri-id'], 'self' => false]);
|
||||
// Update the public contact as well
|
||||
$fields['prvkey'] = null;
|
||||
$fields['self'] = false;
|
||||
self::update($fields, ['uid' => 0, 'nurl' => $self['nurl']]);
|
||||
|
||||
// Update the profile
|
||||
$fields = [
|
||||
|
@ -799,20 +797,14 @@ class Contact
|
|||
public static function remove($id)
|
||||
{
|
||||
// We want just to make sure that we don't delete our "self" contact
|
||||
$contact = DBA::selectFirst('contact', ['uri-id', 'photo', 'thumb', 'micro', 'uid'], ['id' => $id, 'self' => false]);
|
||||
$contact = DBA::selectFirst('contact', ['uid'], ['id' => $id, 'self' => false]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||
|
||||
// Archive the contact
|
||||
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
|
||||
Worker::add(PRIORITY_MEDIUM, 'Contact\Remove', $id);
|
||||
}
|
||||
|
@ -835,10 +827,8 @@ class Contact
|
|||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
if (!empty($cdata['public'])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::removeSharer($contact);
|
||||
|
@ -864,10 +854,8 @@ class Contact
|
|||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
if (!empty($cdata['public'])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::removeFollower($contact);
|
||||
|
@ -890,29 +878,19 @@ class Contact
|
|||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND]) && !empty($cdata['public'])) {
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND]) && !empty($cdata['public'])) {
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1482,7 +1460,7 @@ class Contact
|
|||
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
||||
|
||||
if ($pager->getStart() == 0) {
|
||||
$cdata = self::getPublicAndUserContactID($cid, local_user());
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
|
||||
if (!empty($cdata['public'])) {
|
||||
$pinned = Post\Collection::selectToArrayForContact($cdata['public'], Post\Collection::FEATURED, $fields);
|
||||
$items = array_merge($items, $pinned);
|
||||
|
@ -1495,7 +1473,7 @@ class Contact
|
|||
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
|
||||
|
||||
if ($pager->getStart() == 0) {
|
||||
$cdata = self::getPublicAndUserContactID($cid, local_user());
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
|
||||
if (!empty($cdata['public'])) {
|
||||
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
||||
$cdata['public'], Post\Collection::FEATURED];
|
||||
|
@ -1589,24 +1567,16 @@ class Contact
|
|||
return;
|
||||
}
|
||||
|
||||
if (Network::isLocalLink($contact['url'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
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']))) {
|
||||
Logger::info('Adding avatar cache', ['id' => $cid, 'contact' => $contact]);
|
||||
self::updateAvatar($cid, $contact['avatar'], true);
|
||||
return;
|
||||
}
|
||||
} elseif (Photo::isPhotoURI($contact['photo']) || Photo::isPhotoURI($contact['thumb']) || Photo::isPhotoURI($contact['micro'])) {
|
||||
Logger::info('Replacing legacy avatar cache', ['id' => $cid, 'contact' => $contact]);
|
||||
} elseif (!empty($contact['photo']) || !empty($contact['thumb']) || !empty($contact['micro'])) {
|
||||
Logger::info('Removing 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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1623,27 +1593,6 @@ class Contact
|
|||
private static function getAvatarPath(array $contact, string $size, $no_update = false)
|
||||
{
|
||||
$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'] ?? '');
|
||||
}
|
||||
|
||||
|
@ -1708,9 +1657,7 @@ class Contact
|
|||
return $contact;
|
||||
}
|
||||
|
||||
$local = !empty($contact['url']) && Network::isLocalLink($contact['url']);
|
||||
|
||||
if (!$local && !empty($contact['id']) && !empty($contact['avatar'])) {
|
||||
if (!empty($contact['id']) && !empty($contact['avatar'])) {
|
||||
self::updateAvatar($contact['id'], $contact['avatar'], true);
|
||||
|
||||
$new_contact = self::getById($contact['id'], $contact_fields);
|
||||
|
@ -1718,8 +1665,6 @@ class Contact
|
|||
// We only update the cache fields
|
||||
$contact = array_merge($contact, $new_contact);
|
||||
}
|
||||
} elseif ($local && !empty($contact['avatar'])) {
|
||||
return $contact;
|
||||
}
|
||||
|
||||
/// add the default avatars if the fields aren't filled
|
||||
|
@ -1854,7 +1799,7 @@ class Contact
|
|||
{
|
||||
// We have to fetch the "updated" variable when it wasn't provided
|
||||
// The parameter can be provided to improve performance
|
||||
if (empty($updated)) {
|
||||
if (empty($updated) || empty($guid)) {
|
||||
$account = DBA::selectFirst('account-user-view', ['updated', 'guid'], ['id' => $cid]);
|
||||
$updated = $account['updated'] ?? '';
|
||||
$guid = $account['guid'] ?? '';
|
||||
|
@ -1956,7 +1901,7 @@ class Contact
|
|||
*/
|
||||
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', 'uri-id'],
|
||||
$contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'xmpp', 'addr', 'nurl', 'url', 'network'],
|
||||
['id' => $cid, 'self' => false]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
return;
|
||||
|
@ -1997,8 +1942,6 @@ class Contact
|
|||
}
|
||||
|
||||
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL]) || $cache_avatar) {
|
||||
Avatar::deleteCache($contact);
|
||||
|
||||
if ($default_avatar && Proxy::isLocalImage($avatar)) {
|
||||
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
||||
'photo' => $avatar,
|
||||
|
@ -2050,8 +1993,9 @@ class Contact
|
|||
}
|
||||
} else {
|
||||
Photo::delete(['uid' => $uid, 'contact-id' => $cid, 'photo-type' => Photo::CONTACT_AVATAR]);
|
||||
$fields = Avatar::fetchAvatarContact($contact, $avatar, $force);
|
||||
$update = ($avatar . $fields['photo'] . $fields['thumb'] . $fields['micro'] != $contact['avatar'] . $contact['photo'] . $contact['thumb'] . $contact['micro']) || $force;
|
||||
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
||||
'photo' => '', 'thumb' => '', 'micro' => ''];
|
||||
$update = ($avatar != $contact['avatar'] . $contact['photo'] . $contact['thumb'] . $contact['micro']) || $force;
|
||||
}
|
||||
|
||||
if (!$update) {
|
||||
|
@ -2330,10 +2274,7 @@ class Contact
|
|||
|
||||
if ($uid == 0) {
|
||||
if ($ret['network'] == Protocol::ACTIVITYPUB) {
|
||||
$apcontact = APContact::getByURL($ret['url'], false);
|
||||
if (!empty($apcontact['featured'])) {
|
||||
Worker::add(PRIORITY_LOW, 'FetchFeaturedPosts', $ret['url']);
|
||||
}
|
||||
ActivityPub\Processor::fetchFeaturedPosts($ret['url']);
|
||||
}
|
||||
|
||||
$ret['last-item'] = Probe::getLastUpdate($ret);
|
||||
|
@ -2543,11 +2484,6 @@ class Contact
|
|||
} else {
|
||||
$probed = true;
|
||||
$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)) {
|
||||
|
@ -2726,8 +2662,6 @@ class Contact
|
|||
$contact = DBA::selectFirst('contact', [], ['id' => $cid]);
|
||||
}
|
||||
|
||||
self::clearFollowerFollowingEndpointCache($importer['uid']);
|
||||
|
||||
if (!empty($contact)) {
|
||||
if (!empty($contact['pending'])) {
|
||||
Logger::info('Pending contact request already exists.', ['url' => $url, 'uid' => $importer['uid']]);
|
||||
|
@ -2851,9 +2785,7 @@ class Contact
|
|||
return;
|
||||
}
|
||||
|
||||
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||
|
||||
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]);
|
||||
}
|
||||
|
@ -2867,8 +2799,6 @@ class Contact
|
|||
*/
|
||||
public static function removeSharer(array $contact)
|
||||
{
|
||||
self::clearFollowerFollowingEndpointCache($contact['uid']);
|
||||
|
||||
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
|
|
|
@ -42,7 +42,6 @@ use Friendica\Util\Map;
|
|||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Worker\Delivery;
|
||||
use LanguageDetection\Language;
|
||||
|
||||
|
@ -88,16 +87,14 @@ class Item
|
|||
'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language',
|
||||
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global',
|
||||
'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-updated',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
||||
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type',
|
||||
'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network',
|
||||
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
|
||||
'writable', 'self', 'cid', 'alias',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||
'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'
|
||||
];
|
||||
|
||||
|
@ -728,7 +725,7 @@ class Item
|
|||
return GRAVITY_UNKNOWN; // Should not happen
|
||||
}
|
||||
|
||||
public static function insert(array $item, int $notify = 0, bool $post_local = true)
|
||||
public static function insert(array $item, bool $notify = false, bool $post_local = true)
|
||||
{
|
||||
$orig_item = $item;
|
||||
|
||||
|
@ -742,7 +739,7 @@ class Item
|
|||
$item['protocol'] = Conversation::PARCEL_DIRECT;
|
||||
$item['direction'] = Conversation::PUSH;
|
||||
|
||||
if (is_int($notify) && in_array($notify, PRIORITIES)) {
|
||||
if (in_array($notify, PRIORITIES)) {
|
||||
$priority = $notify;
|
||||
}
|
||||
} else {
|
||||
|
@ -1082,7 +1079,7 @@ class Item
|
|||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
|
||||
$content_warning = BBCode::getAbstract($item['body'], Protocol::ACTIVITYPUB);
|
||||
if (!empty($content_warning) && empty($item['content-warning'])) {
|
||||
$item['content-warning'] = BBCode::toPlaintext($content_warning);
|
||||
$item['content-warning'] = $content_warning;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1218,30 +1215,9 @@ class Item
|
|||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -1449,7 +1425,7 @@ class Item
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (($uri_id != $item['thr-parent-id']) && (($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||
if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||
// Fetch the origin user for the post
|
||||
$origin_uid = self::GetOriginUidForUriId($item['thr-parent-id'], $uid);
|
||||
if (is_null($origin_uid)) {
|
||||
|
@ -2716,7 +2692,7 @@ class Item
|
|||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @todo Remove reference, simply return "rendered-html" and "rendered-hash"
|
||||
*/
|
||||
private static function putInCache(&$item)
|
||||
public static function putInCache(&$item)
|
||||
{
|
||||
// Save original body to prevent addons to modify it
|
||||
$body = $item['body'];
|
||||
|
@ -2729,6 +2705,8 @@ class Item
|
|||
|| $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body)
|
||||
|| DI::config()->get('system', 'ignore_cache')
|
||||
) {
|
||||
self::addRedirToImageTags($item);
|
||||
|
||||
$item['rendered-html'] = BBCode::convertForUriId($item['uri-id'], $item['body']);
|
||||
$item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
|
||||
|
||||
|
@ -2753,6 +2731,31 @@ class Item
|
|||
$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.
|
||||
* If attach is true, also add icons for item attachments.
|
||||
|
@ -2760,7 +2763,6 @@ class Item
|
|||
* @param array $item
|
||||
* @param boolean $attach
|
||||
* @param boolean $is_preview
|
||||
* @param boolean $only_cache
|
||||
* @return string item body html
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
|
@ -2769,7 +2771,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_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, $only_cache = false)
|
||||
public static function prepareBody(array &$item, $attach = false, $is_preview = false)
|
||||
{
|
||||
$a = DI::app();
|
||||
Hook::callAll('prepare_body_init', $item);
|
||||
|
@ -2790,10 +2792,10 @@ class Item
|
|||
$body = $item['body'] ?? '';
|
||||
$shared = BBCode::fetchShareAttributes($body);
|
||||
if (!empty($shared['guid'])) {
|
||||
$shared_item = Post::selectFirst(['uri-id', 'plink', 'has-media'], ['guid' => $shared['guid']]);
|
||||
$shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
|
||||
$shared_uri_id = $shared_item['uri-id'] ?? 0;
|
||||
$shared_links = [strtolower($shared_item['plink'] ?? '')];
|
||||
$shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid'], [], $shared_item['has-media'] ?? false);
|
||||
$shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
|
||||
$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['additional'], 'url'));
|
||||
|
@ -2802,7 +2804,7 @@ class Item
|
|||
$shared_uri_id = 0;
|
||||
$shared_links = [];
|
||||
}
|
||||
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links, $item['has-media']);
|
||||
$attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links);
|
||||
$item['body'] = self::replaceVisualAttachments($attachments, $item['body'] ?? '');
|
||||
|
||||
$item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']);
|
||||
|
@ -2810,10 +2812,6 @@ class Item
|
|||
$item['body'] = $body;
|
||||
$s = $item["rendered-html"];
|
||||
|
||||
if ($only_cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile eventual content filter reasons
|
||||
$filter_reasons = [];
|
||||
if (!$is_preview && public_contact() != $item['author-id']) {
|
||||
|
@ -2859,7 +2857,6 @@ class Item
|
|||
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
||||
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
|
||||
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
|
||||
$s = self::addQuestions($item, $s);
|
||||
|
||||
// Map.
|
||||
if (strpos($s, '<div class="map">') !== false && !empty($item['coord'])) {
|
||||
|
@ -2930,17 +2927,9 @@ class Item
|
|||
|
||||
foreach ($attachments['visual'] as $attachment) {
|
||||
if (!empty($attachment['preview'])) {
|
||||
$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);
|
||||
$body = str_replace($attachment['preview'], Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE), $body);
|
||||
} elseif ($attachment['filetype'] == 'image') {
|
||||
$proxy = Post\Media::getUrlForId($attachment['id']);
|
||||
$search = ['[img=' . $attachment['url'] . ']', ']' . $attachment['url'] . '[/img]'];
|
||||
$replace = ['[img=' . $proxy . ']', ']' . $proxy . '[/img]'];
|
||||
|
||||
$body = str_replace($search, $replace, $body);
|
||||
$body = str_replace($attachment['url'], Post\Media::getUrlForId($attachment['id']), $body);
|
||||
}
|
||||
}
|
||||
DI::profiler()->stopRecording();
|
||||
|
@ -3193,50 +3182,6 @@ class Item
|
|||
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
|
||||
*
|
||||
|
@ -3260,12 +3205,6 @@ class Item
|
|||
'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)) {
|
||||
$ret['href'] = DI::baseUrl()->remove($plink);
|
||||
$ret['title'] = DI::l10n()->t('Link to source');
|
||||
|
|
|
@ -704,19 +704,10 @@ class Photo
|
|||
}
|
||||
$image_uri = substr($image_uri, strrpos($image_uri, '/') + 1);
|
||||
$image_uri = substr($image_uri, 0, strpos($image_uri, '-'));
|
||||
return trim($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));
|
||||
if (!strlen($image_uri)) {
|
||||
return '';
|
||||
}
|
||||
return $image_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -111,11 +111,6 @@ class Category
|
|||
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
|
||||
*
|
||||
|
|
|
@ -24,8 +24,6 @@ namespace Friendica\Model\Post;
|
|||
use Friendica\Database\DBA;
|
||||
use BadMethodCallException;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\DI;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
|
||||
class Collection
|
||||
{
|
||||
|
@ -36,19 +34,14 @@ class Collection
|
|||
*
|
||||
* @param integer $uri_id
|
||||
* @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, int $cache_uid = 0)
|
||||
public static function add(int $uri_id, int $type)
|
||||
{
|
||||
if (empty($uri_id)) {
|
||||
throw new BadMethodCallException('Empty URI_id');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,19 +49,14 @@ class Collection
|
|||
*
|
||||
* @param integer $uri_id
|
||||
* @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, int $cache_uid = 0)
|
||||
public static function remove(int $uri_id, int $type)
|
||||
{
|
||||
if (empty($uri_id)) {
|
||||
throw new BadMethodCallException('Empty URI_id');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
<?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,17 +547,12 @@ class Media
|
|||
* @param int $uri_id
|
||||
* @param string $guid
|
||||
* @param array $links list of links that shouldn't be added
|
||||
* @param bool $has_media
|
||||
* @return array attachments
|
||||
*/
|
||||
public static function splitAttachments(int $uri_id, string $guid = '', array $links = [], bool $has_media = true)
|
||||
public static function splitAttachments(int $uri_id, string $guid = '', array $links = [])
|
||||
{
|
||||
$attachments = ['visual' => [], 'link' => [], 'additional' => []];
|
||||
|
||||
if (!$has_media) {
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
$media = self::getByURIId($uri_id);
|
||||
if (empty($media)) {
|
||||
return $attachments;
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
namespace Friendica\Model\Post;
|
||||
|
||||
use \BadMethodCallException;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Database\DBStructure;
|
||||
use Friendica\Model\Post;
|
||||
|
||||
class Thread
|
||||
{
|
||||
|
|
|
@ -308,7 +308,7 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
$notification = DI::notificationFactory()->createForUser(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForUser(
|
||||
$uid,
|
||||
$item['vid'],
|
||||
$type,
|
||||
|
@ -336,7 +336,7 @@ class UserNotification
|
|||
*/
|
||||
public static function insertNotification(int $actor, string $verb, int $uid): bool
|
||||
{
|
||||
$notification = DI::notificationFactory()->createForRelationship(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForRelationship(
|
||||
$uid,
|
||||
$actor,
|
||||
$verb
|
||||
|
|
|
@ -220,7 +220,7 @@ class Profile
|
|||
public static function load(App $a, string $nickname, bool $show_contacts = true)
|
||||
{
|
||||
$profile = User::getOwnerDataByNick($nickname);
|
||||
if (empty($profile) || $profile['account_removed']) {
|
||||
if (empty($profile)) {
|
||||
Logger::info('profile error: ' . DI::args()->getQueryString());
|
||||
return [];
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ class Profile
|
|||
|
||||
DI::page()['title'] = $profile['name'] . ' @ ' . DI::config()->get('config', 'sitename');
|
||||
|
||||
if (!local_user()) {
|
||||
if (!DI::pConfig()->get(local_user(), 'system', 'always_my_theme')) {
|
||||
$a->setCurrentTheme($profile['theme']);
|
||||
$a->setCurrentMobileTheme(DI::pConfig()->get($a->getProfileOwner(), 'system', 'mobile_theme'));
|
||||
}
|
||||
|
@ -880,17 +880,23 @@ class Profile
|
|||
*
|
||||
* 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
|
||||
* settings take precedence; unless a local user is logged in which means they don't
|
||||
* want to see anybody else's theme settings except their own while on this site.
|
||||
* settings take precedence; unless a local user sets the "always_my_theme"
|
||||
* system pconfig, which means they don't want to see anybody else's theme
|
||||
* settings except their own while on this site.
|
||||
*
|
||||
* @param App $a
|
||||
* @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
|
||||
*/
|
||||
public static function getThemeUid(App $a): int
|
||||
public static function getThemeUid(App $a)
|
||||
{
|
||||
return local_user() ?: $a->getProfileOwner();
|
||||
$uid = !empty($a->getProfileOwner()) ? intval($a->getProfileOwner()) : 0;
|
||||
if (local_user() && (DI::pConfig()->get(local_user(), 'system', 'always_my_theme') || !$uid)) {
|
||||
return local_user();
|
||||
}
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -143,7 +143,17 @@ class Subscription
|
|||
{
|
||||
$type = \Friendica\Factory\Api\Mastodon\Notification::getType($Notification);
|
||||
|
||||
if (DI::notify()->NotifyOnDesktop($Notification, $type)) {
|
||||
$desktop_notification = !in_array($type, [Notification::TYPE_RESHARE, Notification::TYPE_LIKE]);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -224,18 +224,15 @@ class Tag
|
|||
{
|
||||
$fields = ['name' => substr($name, 0, 96), 'url' => $url];
|
||||
|
||||
$tag = DBA::selectFirst('tag', ['id', 'type'], $fields);
|
||||
if (DBA::isResult($tag)) {
|
||||
if (empty($tag['type']) && !empty($type)) {
|
||||
DBA::update('tag', ['type' => $type], $fields);
|
||||
}
|
||||
return $tag['id'];
|
||||
}
|
||||
|
||||
if (!empty($type)) {
|
||||
$fields['type'] = $type;
|
||||
}
|
||||
|
||||
$tag = DBA::selectFirst('tag', ['id'], $fields);
|
||||
if (DBA::isResult($tag)) {
|
||||
return $tag['id'];
|
||||
}
|
||||
|
||||
DBA::insert('tag', $fields, Database::INSERT_IGNORE);
|
||||
$tid = DBA::lastInsertId();
|
||||
if (!empty($tid)) {
|
||||
|
@ -276,7 +273,7 @@ class Tag
|
|||
public static function getFromBody(string $body, string $tags = null)
|
||||
{
|
||||
if (is_null($tags)) {
|
||||
$tags = self::TAG_CHARACTER[self::HASHTAG] . self::TAG_CHARACTER[self::MENTION] . self::TAG_CHARACTER[self::EXCLUSIVE_MENTION];
|
||||
$tags = self::TAG_CHARACTER[self::HASHTAG] . self::TAG_CHARACTER[self::MENTION] . self::TAG_CHARACTER[self::EXCLUSIVE_MENTION];
|
||||
}
|
||||
|
||||
if (!preg_match_all("/([" . $tags . "])\[url\=([^\[\]]*)\]([^\[\]]*)\[\/url\]/ism", $body, $result, PREG_SET_ORDER)) {
|
||||
|
@ -298,29 +295,16 @@ class Tag
|
|||
{
|
||||
Logger::info('Check for tags', ['uri-id' => $uriid, 'hash' => $tags, 'callstack' => System::callstack()]);
|
||||
|
||||
if (is_null($tags)) {
|
||||
$tags = self::TAG_CHARACTER[self::HASHTAG] . self::TAG_CHARACTER[self::MENTION] . self::TAG_CHARACTER[self::EXCLUSIVE_MENTION];
|
||||
$result = self::getFromBody($body, $tags);
|
||||
if (empty($result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
Logger::info('Found tags', ['uri-id' => $uriid, 'hash' => $tags, 'result' => $result]);
|
||||
|
||||
foreach (self::getFromBody($body, $tags) as $tag) {
|
||||
foreach ($result as $tag) {
|
||||
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,7 +43,6 @@ class Federation extends BaseAdmin
|
|||
'diaspora' => ['name' => 'Diaspora', 'color' => '#a1a1a1'], // logo is black and white, makes a gray
|
||||
'funkwhale' => ['name' => 'Funkwhale', 'color' => '#4082B4'], // From the homepage
|
||||
'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
|
||||
'hubzilla' => ['name' => 'Hubzilla/Red Matrix', 'color' => '#43488a'], // blue from the logo
|
||||
'lemmy' => ['name' => 'Lemmy', 'color' => '#00c853'], // Green from the page
|
||||
|
@ -155,7 +154,7 @@ class Federation extends BaseAdmin
|
|||
$versionCounts = self::reformaDiasporaVersions($versionCounts);
|
||||
} elseif ($platform == 'relay') {
|
||||
$versionCounts = self::reformatRelayVersions($versionCounts);
|
||||
} elseif (in_array($platform, ['funkwhale', 'mastodon', 'mobilizon', 'misskey', 'gotosocial'])) {
|
||||
} elseif (in_array($platform, ['funkwhale', 'mastodon', 'mobilizon', 'misskey'])) {
|
||||
$versionCounts = self::removeVersionSuffixes($versionCounts);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
namespace Friendica\Module\Admin;
|
||||
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Module\BaseAdmin;
|
||||
|
||||
class PhpInfo extends BaseAdmin
|
||||
|
@ -31,6 +30,6 @@ class PhpInfo extends BaseAdmin
|
|||
self::checkAdminAccess();
|
||||
|
||||
phpinfo();
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module\Admin;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Relocate;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\Core\System;
|
||||
|
@ -61,6 +60,74 @@ class Site extends BaseAdmin
|
|||
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']) : '');
|
||||
$sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : '');
|
||||
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
|
||||
|
@ -445,9 +512,8 @@ class Site extends BaseAdmin
|
|||
'$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' => Relay::getList(['url']),
|
||||
'$relocate' => DI::l10n()->t('Relocate Node'),
|
||||
'$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'),
|
||||
'$relocate' => DI::l10n()->t('Relocate Instance'),
|
||||
'$relocate_warning' => DI::l10n()->t('<strong>Warning!</strong> Advanced function. Could make this server unreachable.'),
|
||||
'$baseurl' => DI::baseUrl()->get(true),
|
||||
|
||||
// name, label, value, help string, extra data...
|
||||
|
@ -535,6 +601,8 @@ 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.')],
|
||||
'$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_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();
|
||||
}
|
||||
|
||||
Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED, $uid);
|
||||
Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class Unpin extends BaseApi
|
|||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED, $uid);
|
||||
Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Friendica\Module;
|
|||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Attach as MAttach;
|
||||
|
||||
|
@ -73,7 +72,7 @@ class Attach extends BaseModule
|
|||
}
|
||||
|
||||
echo $data;
|
||||
System::exit();
|
||||
exit();
|
||||
// NOTREACHED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ use Friendica\Model;
|
|||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Object\Search\ContactResult;
|
||||
use Friendica\Object\Search\ResultList;
|
||||
use Friendica\Util\Network;
|
||||
|
||||
/**
|
||||
* Base class for search modules
|
||||
|
@ -69,7 +68,7 @@ class BaseSearch extends BaseModule
|
|||
$header = DI::l10n()->t('People Search - %s', $search);
|
||||
|
||||
if (strrpos($search, '@') > 0) {
|
||||
$results = Search::getContactsFromProbe(Network::convertToIdn($search));
|
||||
$results = Search::getContactsFromProbe($search);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,8 +78,6 @@ class BaseSearch extends BaseModule
|
|||
$header = DI::l10n()->t('Forum Search - %s', $search);
|
||||
}
|
||||
|
||||
$search = Network::convertToIdn($search);
|
||||
|
||||
if (DI::mode()->isMobile()) {
|
||||
$itemsPerPage = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
|
||||
DI::config()->get('system', 'itemspage_network_mobile'));
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace Friendica\Module\Contact;
|
|||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
|
@ -108,6 +107,6 @@ class Hovercard extends BaseModule
|
|||
]);
|
||||
|
||||
echo $o;
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module\Debug;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
@ -49,7 +48,7 @@ class ItemBody extends BaseModule
|
|||
if (!empty($item)) {
|
||||
if (DI::mode()->isAjax()) {
|
||||
echo str_replace("\n", '<br />', $item['body']);
|
||||
System::exit();
|
||||
exit();
|
||||
} else {
|
||||
return str_replace("\n", '<br />', $item['body']);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ use Friendica\BaseModule;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Protocol\Feed as ProtocolFeed;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
||||
/**
|
||||
* Provides public Atom feeds
|
||||
|
@ -43,7 +42,7 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Feed extends BaseModule
|
||||
{
|
||||
protected function rawContent(array $request = [])
|
||||
protected function content(array $request = []): string
|
||||
{
|
||||
$last_update = $request['last_update'] ?? '';
|
||||
$nocache = !empty($request['nocache']) && local_user();
|
||||
|
@ -67,11 +66,6 @@ class Feed extends BaseModule
|
|||
$type = 'posts';
|
||||
}
|
||||
|
||||
$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);
|
||||
System::httpExit(ProtocolFeed::atom($this->parameters['nickname'], $last_update, 10, $type, $nocache, true), Response::TYPE_ATOM);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -360,7 +360,7 @@ class Group extends BaseModule
|
|||
if ($change) {
|
||||
$tpl = Renderer::getMarkupTemplate('groupeditor.tpl');
|
||||
echo Renderer::replaceMacros($tpl, $context);
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
return Renderer::replaceMacros($tpl, $context);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module\HTTPException;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
@ -48,7 +47,7 @@ class PageNotFound extends BaseModule
|
|||
$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.
|
||||
if (!empty($queryString) && preg_match('/{[0-9]}/', $queryString) !== 0) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!empty($queryString) && ($queryString === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
|
||||
|
|
|
@ -60,9 +60,9 @@ class Pin extends BaseModule
|
|||
$pinned = !$item['featured'];
|
||||
|
||||
if ($pinned) {
|
||||
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED, local_user());
|
||||
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
|
||||
} else {
|
||||
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED, local_user());
|
||||
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED);
|
||||
}
|
||||
|
||||
// See if we've been passed a return path to redirect to
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
|
@ -36,8 +35,6 @@ use Friendica\Model\User;
|
|||
*/
|
||||
class NoScrape extends BaseModule
|
||||
{
|
||||
const CACHEKEY = 'noscrape:';
|
||||
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$a = DI::app();
|
||||
|
@ -58,12 +55,6 @@ class NoScrape extends BaseModule
|
|||
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 = [
|
||||
'addr' => $owner['addr'],
|
||||
'nick' => $which,
|
||||
|
@ -135,8 +126,6 @@ class NoScrape extends BaseModule
|
|||
}
|
||||
}
|
||||
|
||||
DI::cache()->set($cachekey, $json_info, Duration::DAY);
|
||||
|
||||
System::jsonExit($json_info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,9 +187,6 @@ class Ping extends BaseModule
|
|||
$owner = User::getOwnerDataById(local_user());
|
||||
|
||||
$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])) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Friendica\Module;
|
|||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Content;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
|
@ -44,14 +43,14 @@ class Oembed extends BaseModule
|
|||
if (DI::args()->getArgv()[1] == 'b2h') {
|
||||
$url = ["", trim(hex2bin($_REQUEST['url']))];
|
||||
echo Content\OEmbed::replaceCallback($url);
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// Unused form: /oembed/h2b?text=...
|
||||
if (DI::args()->getArgv()[1] == 'h2b') {
|
||||
$text = trim(hex2bin($_REQUEST['text']));
|
||||
echo Content\OEmbed::HTML2BBCode($text);
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
// @TODO: Replace with parameter from router
|
||||
|
@ -69,6 +68,6 @@ class Oembed extends BaseModule
|
|||
echo $j->html;
|
||||
echo '</body></html>';
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\APContact;
|
||||
|
@ -165,7 +164,8 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
} else {
|
||||
echo $o . $receivers;
|
||||
}
|
||||
System::exit();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,7 +32,6 @@ use Friendica\Model\Post;
|
|||
use Friendica\Model\Profile;
|
||||
use Friendica\Core\Storage\Type\ExternalResource;
|
||||
use Friendica\Core\Storage\Type\SystemResource;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
|
@ -225,7 +224,7 @@ class Photo extends BaseModule
|
|||
'output' => number_format($output, 3), 'rest' => number_format($rest, 3)]);
|
||||
}
|
||||
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
private static function getPhotoByid(int $id, $type, $customsize)
|
||||
|
@ -305,12 +304,10 @@ class Photo extends BaseModule
|
|||
$photo = MPhoto::selectFirst([], ['resource-id' => $resourceid], ['order' => ['scale']]);
|
||||
if (!empty($photo)) {
|
||||
return $photo;
|
||||
} else {
|
||||
$url = $contact['avatar'];
|
||||
}
|
||||
} else {
|
||||
$url = $contact['photo'];
|
||||
}
|
||||
// We continue with the avatar link when the photo link is invalid
|
||||
$url = $contact['avatar'];
|
||||
} elseif (!empty($contact['avatar'])) {
|
||||
$url = $contact['avatar'];
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class Profile extends BaseProfile
|
|||
protected function rawContent(array $request = [])
|
||||
{
|
||||
if (ActivityPub::isRequest()) {
|
||||
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $this->parameters['nickname'], 'account_removed' => false]);
|
||||
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $this->parameters['nickname']]);
|
||||
if (DBA::isResult($user)) {
|
||||
try {
|
||||
$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('Cache-Control: max-age=31536000');
|
||||
echo $img->asString();
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\System;
|
||||
|
||||
/**
|
||||
* Return the default robots.txt
|
||||
|
@ -45,6 +44,6 @@ class RobotsTxt extends BaseModule
|
|||
foreach ($allDisalloweds as $disallowed) {
|
||||
echo 'Disallow: ' . $disallowed . PHP_EOL;
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ use Friendica\Model\Post;
|
|||
use Friendica\Model\Tag;
|
||||
use Friendica\Module\BaseSearch;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Network;
|
||||
|
||||
class Index extends BaseSearch
|
||||
{
|
||||
|
@ -227,8 +226,7 @@ class Index extends BaseSearch
|
|||
*/
|
||||
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);
|
||||
|
||||
if (!$isUrl && !$isAddr) {
|
||||
|
@ -276,8 +274,6 @@ class Index extends BaseSearch
|
|||
return;
|
||||
}
|
||||
|
||||
$search = Network::convertToIdn($search);
|
||||
|
||||
if (local_user()) {
|
||||
// Post URL search
|
||||
$item_id = Item::fetchByLink($search, local_user());
|
||||
|
|
|
@ -349,7 +349,7 @@ class Account extends BaseSettings
|
|||
// "http" or "@" to be present in the string.
|
||||
// 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'])) {
|
||||
Worker::add(PRIORITY_MEDIUM, 'AddContact', local_user(), $csvRow[0]);
|
||||
Worker::add(PRIORITY_LOW, 'AddContact', $_SESSION['uid'], $csvRow[0]);
|
||||
} else {
|
||||
Logger::notice('Invalid account', ['url' => $csvRow[0]]);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Friendica\Module\Settings;
|
|||
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Database\DBStructure;
|
||||
use Friendica\DI;
|
||||
|
@ -113,7 +112,8 @@ class UserExport extends BaseSettings
|
|||
self::exportContactsAsCSV(local_user());
|
||||
break;
|
||||
}
|
||||
System::exit();
|
||||
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +45,7 @@ class Theme extends BaseModule
|
|||
if (file_exists("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,
|
||||
]);
|
||||
}
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class Network extends NetworkModule
|
|||
protected function rawContent(array $request = [])
|
||||
{
|
||||
if (!isset($_GET['p']) || !isset($_GET['item'])) {
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$this->parseRequest($_GET);
|
||||
|
|
|
@ -25,10 +25,7 @@ use Friendica\App\BaseURL;
|
|||
use Friendica\BaseFactory;
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
use Friendica\Contact\LocalRelationship\Repository\LocalRelationship;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\Plaintext;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Cache\Capability\ICanCache;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Post;
|
||||
|
@ -46,17 +43,14 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
private $l10n;
|
||||
/** @var LocalRelationship */
|
||||
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, ICanCache $cache)
|
||||
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Core\L10n $l10n, \Friendica\Contact\LocalRelationship\Repository\LocalRelationship $localRelationshipRepo, LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->l10n = $l10n;
|
||||
$this->localRelationshipRepo = $localRelationshipRepo;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function createFromTableRow(array $row): Entity\Notification
|
||||
|
@ -113,12 +107,6 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
{
|
||||
$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']);
|
||||
if (empty($causer)) {
|
||||
$this->logger->info('Causer not found', ['contact' => $Notification->actorId]);
|
||||
|
@ -182,10 +170,11 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
|
||||
$link = $this->baseUrl . '/display/' . urlencode($link_item['guid']);
|
||||
|
||||
$body = BBCode::toPlaintext($item['body'], false);
|
||||
$title = Plaintext::shorten($body, 70);
|
||||
if (!empty($title)) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||
$content = Plaintext::getPost($item, 70);
|
||||
if (!empty($content['text'])) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
||||
} else {
|
||||
$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]);
|
||||
|
@ -317,7 +306,6 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
'[url=' . $link . ']' . $title . '[/url]',
|
||||
'[url=' . $author['url'] . ']' . $author['name'] . '[/url]');
|
||||
$message['link'] = $link;
|
||||
$this->cache->set($cachekey, $message, Duration::HOUR);
|
||||
} else {
|
||||
$this->logger->debug('Unhandled notification', ['notification' => $Notification]);
|
||||
}
|
||||
|
|
|
@ -23,10 +23,8 @@ namespace Friendica\Navigation\Notifications\Repository;
|
|||
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\BaseRepository;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\Plaintext;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
|
@ -38,7 +36,6 @@ use Friendica\Navigation\Notifications\Entity;
|
|||
use Friendica\Navigation\Notifications\Exception;
|
||||
use Friendica\Navigation\Notifications\Factory;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Object\Api\Mastodon\Notification;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Emailer;
|
||||
|
@ -61,9 +58,6 @@ class Notify extends BaseRepository
|
|||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
/** @var IManagePersonalConfigValues */
|
||||
private $pConfig;
|
||||
|
||||
/** @var Emailer */
|
||||
protected $emailer;
|
||||
|
||||
|
@ -72,12 +66,11 @@ class Notify extends BaseRepository
|
|||
|
||||
protected static $table_name = 'notify';
|
||||
|
||||
public function __construct(Database $database, LoggerInterface $logger, L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, Emailer $emailer, Factory\Notification $notification, Factory\Notify $factory = null)
|
||||
public function __construct(Database $database, LoggerInterface $logger, L10n $l10n, BaseURL $baseUrl, IManageConfigValues $config, Emailer $emailer, Factory\Notification $notification, Factory\Notify $factory = null)
|
||||
{
|
||||
$this->l10n = $l10n;
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->config = $config;
|
||||
$this->pConfig = $pConfig;
|
||||
$this->emailer = $emailer;
|
||||
$this->notification = $notification;
|
||||
|
||||
|
@ -308,10 +301,11 @@ class Notify extends BaseRepository
|
|||
|
||||
$item_post_type = Model\Item::postType($item, $l10n);
|
||||
|
||||
$body = BBCode::toPlaintext($item['body'], false);
|
||||
$title = Plaintext::shorten($body, 70);
|
||||
if (!empty($title)) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||
$content = Plaintext::getPost($item, 70);
|
||||
if (!empty($content['text'])) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
||||
} else {
|
||||
$title = '';
|
||||
}
|
||||
|
||||
// First go for the general message
|
||||
|
@ -657,27 +651,6 @@ class Notify extends BaseRepository
|
|||
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)
|
||||
{
|
||||
$this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
|
||||
|
@ -737,10 +710,11 @@ class Notify extends BaseRepository
|
|||
return false;
|
||||
}
|
||||
|
||||
$body = BBCode::toPlaintext($item['body'], false);
|
||||
$title = Plaintext::shorten($body, 70);
|
||||
if (!empty($title)) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $title)) . '"';
|
||||
$content = Plaintext::getPost($item, 70);
|
||||
if (!empty($content['text'])) {
|
||||
$title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"';
|
||||
} else {
|
||||
$title = $item['title'];
|
||||
}
|
||||
|
||||
// Some mail software relies on subject field for threading.
|
||||
|
|
|
@ -140,7 +140,7 @@ class HttpClient implements ICanSendHttpRequests
|
|||
}
|
||||
};
|
||||
|
||||
if (empty($conf[HttpClientOptions::HEADERS]['Accept']) && in_array($method, ['get', 'head'])) {
|
||||
if (empty($conf[HttpClientOptions::HEADERS]['Accept'])) {
|
||||
$this->logger->info('Accept header was missing, using default.', ['url' => $url, 'callstack' => System::callstack()]);
|
||||
$conf[HttpClientOptions::HEADERS]['Accept'] = HttpClientAccept::DEFAULT;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,6 @@ class Probe
|
|||
// At first remove leading and trailing junk
|
||||
$rawUri = trim($rawUri, "@#?:/ \t\n\r\0\x0B");
|
||||
|
||||
$rawUri = Network::convertToIdn($rawUri);
|
||||
|
||||
$uri = new Uri($rawUri);
|
||||
if (!$uri->getScheme()) {
|
||||
return $uri->__toString();
|
||||
|
@ -245,6 +243,49 @@ class Probe
|
|||
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
|
||||
*
|
||||
|
|
|
@ -159,24 +159,15 @@ class Image
|
|||
}
|
||||
|
||||
$this->valid = false;
|
||||
try {
|
||||
$this->image = @imagecreatefromstring($data);
|
||||
if ($this->image !== false) {
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
$this->valid = true;
|
||||
imagealphablending($this->image, false);
|
||||
imagesavealpha($this->image, true);
|
||||
$this->image = @imagecreatefromstring($data);
|
||||
if ($this->image !== false) {
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
$this->valid = true;
|
||||
imagealphablending($this->image, false);
|
||||
imagesavealpha($this->image, 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 true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -29,10 +29,10 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Post as PostModel;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
|
@ -482,7 +482,7 @@ class Post
|
|||
'profile_url' => $profile_link,
|
||||
'name' => $profile_name,
|
||||
'item_photo_menu_html' => DI::contentItem()->photoMenu($item, $formSecurityToken),
|
||||
'thumb' => DI::baseUrl()->remove(DI::contentItem()->getAuthorAvatar($item)),
|
||||
'thumb' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['author-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
||||
'osparkle' => $osparkle,
|
||||
'sparkle' => $sparkle,
|
||||
'title' => $title,
|
||||
|
@ -499,7 +499,7 @@ class Post
|
|||
'shiny' => $shiny,
|
||||
'owner_self' => $item['author-link'] == Session::get('my_url'),
|
||||
'owner_url' => $this->getOwnerUrl(),
|
||||
'owner_photo' => DI::baseUrl()->remove(DI::contentItem()->getOwnerAvatar($item)),
|
||||
'owner_photo' => DI::baseUrl()->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
|
||||
'owner_name' => $this->getOwnerName(),
|
||||
'plink' => Item::getPlink($item),
|
||||
'browsershare' => $browsershare,
|
||||
|
@ -529,8 +529,8 @@ class Post
|
|||
'thread_level' => $thread_level,
|
||||
'edited' => $edited,
|
||||
'network' => $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'], $item['author-gsid']),
|
||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network']),
|
||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
|
||||
'received' => $item['received'],
|
||||
'commented' => $item['commented'],
|
||||
'created_date' => $item['created'],
|
||||
|
@ -898,7 +898,12 @@ class Post
|
|||
}
|
||||
|
||||
$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')) {
|
||||
$text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n";
|
||||
|
@ -962,7 +967,7 @@ class Post
|
|||
$uid = $conv->getProfileOwner();
|
||||
$parent_uid = $this->getDataValue('uid');
|
||||
|
||||
$owner = User::getOwnerDataById($a->getLoggedInUserId());
|
||||
$contact = Contact::getById($a->getContactId());
|
||||
|
||||
$default_text = $this->getDefaultText();
|
||||
|
||||
|
@ -981,9 +986,9 @@ class Post
|
|||
'$qcomment' => $qcomment,
|
||||
'$default' => $default_text,
|
||||
'$profile_uid' => $uid,
|
||||
'$mylink' => DI::baseUrl()->remove($owner['url'] ?? ''),
|
||||
'$mylink' => DI::baseUrl()->remove($contact['url'] ?? ''),
|
||||
'$mytitle' => DI::l10n()->t('This is you'),
|
||||
'$myphoto' => DI::baseUrl()->remove($owner['thumb'] ?? ''),
|
||||
'$myphoto' => DI::baseUrl()->remove($contact['thumb'] ?? ''),
|
||||
'$comment' => DI::l10n()->t('Comment'),
|
||||
'$submit' => DI::l10n()->t('Submit'),
|
||||
'$loading' => DI::l10n()->t('Loading...'),
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
<?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'])) {
|
||||
$question['end-time'] = DateTimeFormat::utc($activity['question']['end-time']);
|
||||
$question['end-time'] = $activity['question']['end-time'];
|
||||
}
|
||||
|
||||
Post\Question::update($item['uri-id'], $question);
|
||||
|
@ -215,7 +215,6 @@ class Processor
|
|||
return;
|
||||
}
|
||||
|
||||
Post\History::add($item['uri-id'], $item);
|
||||
Item::update($item, ['uri' => $activity['id']]);
|
||||
|
||||
if ($activity['object_type'] == 'as:Event') {
|
||||
|
@ -239,12 +238,8 @@ class Processor
|
|||
$event['edited'] = DateTimeFormat::utc($activity['updated']);
|
||||
$event['summary'] = HTML::toBBCode($activity['name']);
|
||||
$event['desc'] = HTML::toBBCode($activity['content']);
|
||||
if (!empty($activity['start-time'])) {
|
||||
$event['start'] = DateTimeFormat::utc($activity['start-time']);
|
||||
}
|
||||
if (!empty($activity['end-time'])) {
|
||||
$event['finish'] = DateTimeFormat::utc($activity['end-time']);
|
||||
}
|
||||
$event['start'] = $activity['start-time'];
|
||||
$event['finish'] = $activity['end-time'];
|
||||
$event['nofinish'] = empty($event['finish']);
|
||||
$event['location'] = $activity['location'];
|
||||
|
||||
|
@ -563,12 +558,8 @@ class Processor
|
|||
{
|
||||
$event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']);
|
||||
$event['desc'] = HTML::toBBCode($activity['content']);
|
||||
if (!empty($activity['start-time'])) {
|
||||
$event['start'] = DateTimeFormat::utc($activity['start-time']);
|
||||
}
|
||||
if (!empty($activity['end-time'])) {
|
||||
$event['finish'] = DateTimeFormat::utc($activity['end-time']);
|
||||
}
|
||||
$event['start'] = $activity['start-time'];
|
||||
$event['finish'] = $activity['end-time'];
|
||||
$event['nofinish'] = empty($event['finish']);
|
||||
$event['location'] = $activity['location'];
|
||||
$event['cid'] = $item['contact-id'];
|
||||
|
|
|
@ -938,7 +938,7 @@ class Receiver
|
|||
|
||||
// Fetch the receivers for the public and the followers collection
|
||||
if ((($receiver == $followers) || (($receiver == self::PUBLIC_COLLECTION) && !$is_forum)) && !empty($actor)) {
|
||||
$receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target, $profile);
|
||||
$receivers = self::getReceiverForActor($actor, $tags, $receivers, $follower_target);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1000,46 +1000,33 @@ class Receiver
|
|||
* @param array $tags
|
||||
* @param array $receivers
|
||||
* @param integer $target_type
|
||||
* @param array $profile
|
||||
*
|
||||
* @return array with receivers (user id)
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function getReceiverForActor($actor, $tags, $receivers, $target_type, $profile)
|
||||
private static function getReceiverForActor($actor, $tags, $receivers, $target_type)
|
||||
{
|
||||
$basecondition = ['rel' => [Contact::SHARING, Contact::FRIEND, Contact::FOLLOWER],
|
||||
'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];
|
||||
}
|
||||
$condition = DBA::mergeConditions($basecondition, ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($actor), 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]);
|
||||
$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);
|
||||
|
||||
// The queries are split because of performance issues
|
||||
$condition = DBA::mergeConditions($basecondition, ["`alias` IN (?, ?) AND `uid` != ?", Strings::normaliseLink($actor), $actor, 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);
|
||||
}
|
||||
DBA::close($contacts);
|
||||
|
||||
// The queries are split because of performance issues
|
||||
$condition = DBA::mergeConditions($basecondition, ["`alias` IN (?, ?) AND `uid` != ?", Strings::normaliseLink($actor), $actor, 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);
|
||||
return $receivers;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ use Friendica\Protocol\ActivityPub;
|
|||
use Friendica\Protocol\Relay;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
use Friendica\Util\JsonLD;
|
||||
use Friendica\Util\LDSignature;
|
||||
use Friendica\Util\Map;
|
||||
use Friendica\Util\Network;
|
||||
|
@ -58,10 +59,6 @@ use Friendica\Util\XML;
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -154,21 +151,12 @@ class Transmitter
|
|||
* @param string $module The name of the relevant AP endpoint module (followers|following)
|
||||
* @param integer $page Page number
|
||||
* @param string $requester URL of the requester
|
||||
* @param boolean $nocache Wether to bypass caching
|
||||
*
|
||||
* @return array of owners
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getContacts(array $owner, array $rel, string $module, int $page = null, string $requester = null, $nocache = false)
|
||||
public static function getContacts(array $owner, array $rel, string $module, int $page = null, string $requester = null)
|
||||
{
|
||||
if (empty($page)) {
|
||||
$cachekey = self::CACHEKEY_CONTACTS . $module . ':'. $owner['uid'];
|
||||
$result = DI::cache()->get($cachekey);
|
||||
if (!$nocache && !is_null($result)) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
$parameters = [
|
||||
'rel' => $rel,
|
||||
'uid' => $owner['uid'],
|
||||
|
@ -204,10 +192,6 @@ class Transmitter
|
|||
}
|
||||
|
||||
if (!$show_contacts) {
|
||||
if (!empty($cachekey)) {
|
||||
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -232,10 +216,6 @@ class Transmitter
|
|||
$data['orderedItems'] = $list;
|
||||
}
|
||||
|
||||
if (!empty($cachekey)) {
|
||||
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -245,22 +225,13 @@ class Transmitter
|
|||
* @param array $owner Owner array
|
||||
* @param integer $page Page number
|
||||
* @param string $requester URL of requesting account
|
||||
* @param boolean $nocache Wether to bypass caching
|
||||
*
|
||||
* @return array of posts
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function getOutbox(array $owner, int $page = null, string $requester = '', $nocache = false)
|
||||
public static function getOutbox(array $owner, int $page = null, string $requester = '')
|
||||
{
|
||||
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]];
|
||||
|
||||
if (!empty($requester)) {
|
||||
|
@ -322,42 +293,27 @@ class Transmitter
|
|||
$data['orderedItems'] = $list;
|
||||
}
|
||||
|
||||
if (!empty($cachekey)) {
|
||||
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public posts for the given owner
|
||||
*
|
||||
* @param array $owner Owner array
|
||||
* @param integer $page Page number
|
||||
* @param boolean $nocache Wether to bypass caching
|
||||
* @param array $owner Owner array
|
||||
* @param integer $page Page number
|
||||
*
|
||||
* @return array of posts
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function getFeatured(array $owner, int $page = null, $nocache = false)
|
||||
public static function getFeatured(array $owner, int $page = null)
|
||||
{
|
||||
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` = ?)",
|
||||
$owner_cid, Post\Collection::FEATURED];
|
||||
Contact::getIdForURL($owner['url'], 0, false), Post\Collection::FEATURED];
|
||||
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
['uid' => $owner['uid'],
|
||||
'author-id' => $owner_cid,
|
||||
'author-id' => Contact::getIdForURL($owner['url'], 0, false),
|
||||
'private' => [Item::PUBLIC, Item::UNLISTED],
|
||||
'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
|
||||
'network' => Protocol::FEDERATED,
|
||||
|
@ -378,36 +334,30 @@ class Transmitter
|
|||
}
|
||||
|
||||
if (empty($page)) {
|
||||
$items = Post::select(['id'], $condition, ['limit' => 20, 'order' => ['created' => true]]);
|
||||
$data['first'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=1';
|
||||
} else {
|
||||
$data['type'] = 'OrderedCollectionPage';
|
||||
$list = [];
|
||||
|
||||
$items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
|
||||
}
|
||||
$list = [];
|
||||
while ($item = Post::fetch($items)) {
|
||||
$activity = self::createActivityFromItem($item['id'], true);
|
||||
$activity['type'] = $activity['type'] == 'Update' ? 'Create' : $activity['type'];
|
||||
|
||||
while ($item = Post::fetch($items)) {
|
||||
$activity = self::createActivityFromItem($item['id'], true);
|
||||
$activity['type'] = $activity['type'] == 'Update' ? 'Create' : $activity['type'];
|
||||
|
||||
// Only list "Create" activity objects here, no reshares
|
||||
if (!empty($activity['object']) && ($activity['type'] == 'Create')) {
|
||||
$list[] = $activity['object'];
|
||||
// Only list "Create" activity objects here, no reshares
|
||||
if (!empty($activity['object']) && ($activity['type'] == 'Create')) {
|
||||
$list[] = $activity['object'];
|
||||
}
|
||||
}
|
||||
}
|
||||
DBA::close($items);
|
||||
DBA::close($items);
|
||||
|
||||
if (count($list) == 20) {
|
||||
$data['next'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=' . ($page + 1);
|
||||
}
|
||||
if (count($list) == 20) {
|
||||
$data['next'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=' . ($page + 1);
|
||||
}
|
||||
|
||||
if (!empty($page)) {
|
||||
$data['partOf'] = DI::baseUrl() . '/featured/' . $owner['nickname'];
|
||||
}
|
||||
|
||||
$data['orderedItems'] = $list;
|
||||
|
||||
if (!empty($cachekey)) {
|
||||
DI::cache()->set($cachekey, $data, Duration::DAY);
|
||||
$data['orderedItems'] = $list;
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
|
@ -341,15 +341,4 @@ class Relay
|
|||
// It should never happen that we arrive here
|
||||
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,9 +27,7 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\APContact;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\ItemURI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPClient\Capability\ICanHandleHttpResponses;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
|
||||
|
@ -265,21 +263,21 @@ class HTTPSignature
|
|||
*/
|
||||
|
||||
/**
|
||||
* Post given data to a target for a user, returns the result class
|
||||
* 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 ICanHandleHttpResponses
|
||||
* @return boolean Was the transmission successful?
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function post(array $data, string $target, int $uid): ICanHandleHttpResponses
|
||||
public static function transmit($data, $target, $uid)
|
||||
{
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
|
||||
if (!$owner) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
$content = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
@ -306,32 +304,16 @@ class HTTPSignature
|
|||
|
||||
$headers['Content-Type'] = 'application/activity+json';
|
||||
|
||||
$postResult = DI::httpClient()->post($target, $content, $headers, DI::config()->get('system', 'curl_timeout'));
|
||||
$postResult = DI::httpClient()->post($target, $content, $headers);
|
||||
$return_code = $postResult->getReturnCode();
|
||||
|
||||
Logger::info('Transmit to ' . $target . ' returned ' . $return_code);
|
||||
|
||||
self::setInboxStatus($target, ($return_code >= 200) && ($return_code <= 299));
|
||||
$success = ($return_code >= 200) && ($return_code <= 299);
|
||||
|
||||
return $postResult;
|
||||
}
|
||||
self::setInboxStatus($target, $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);
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -347,7 +329,7 @@ class HTTPSignature
|
|||
|
||||
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
||||
if (!DBA::isResult($status)) {
|
||||
DBA::insert('inbox-status', ['url' => $url, 'uri-id' => ItemURI::getIdByURI($url), 'created' => $now, 'shared' => $shared], Database::INSERT_IGNORE);
|
||||
DBA::insert('inbox-status', ['url' => $url, 'created' => $now, 'shared' => $shared], Database::INSERT_IGNORE);
|
||||
$status = DBA::selectFirst('inbox-status', [], ['url' => $url]);
|
||||
}
|
||||
|
||||
|
@ -387,10 +369,6 @@ class HTTPSignature
|
|||
$fields['archive'] = false;
|
||||
}
|
||||
|
||||
if (empty($status['uri-id'])) {
|
||||
$fields['uri-id'] = ItemURI::getIdByURI($url);
|
||||
}
|
||||
|
||||
DBA::update('inbox-status', $fields, ['url' => $url]);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ namespace Friendica\Util;
|
|||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Logger;
|
||||
use Exception;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
|
||||
/**
|
||||
|
@ -70,7 +69,7 @@ class JsonLD
|
|||
|
||||
if ($recursion > 5) {
|
||||
Logger::error('jsonld bomb detected at: ' . $url);
|
||||
System::exit();
|
||||
exit();
|
||||
}
|
||||
|
||||
$result = DI::cache()->get('documentLoader:' . $url);
|
||||
|
|
|
@ -26,7 +26,6 @@ use Friendica\Core\Logger;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Network\HTTPException\NotModifiedException;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
|
||||
class Network
|
||||
{
|
||||
|
@ -437,7 +436,7 @@ class Network
|
|||
* @param array $parsed URL parts
|
||||
*
|
||||
* @return string The glued URL.
|
||||
* @deprecated since version 2021.12, use GuzzleHttp\Psr7\Uri::fromParts($parts) instead
|
||||
* @deprecated since version 2021.12, use a UriInterface object like GuzzleHttp\Psr7\Uri instead
|
||||
*/
|
||||
public static function unparseURL(array $parsed)
|
||||
{
|
||||
|
@ -463,29 +462,6 @@ class Network
|
|||
(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
|
||||
|
|
|
@ -485,8 +485,9 @@ class Strings
|
|||
* @param string $regex
|
||||
* @param callable $callback
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function performWithEscapedBlocks(string $text, string $regex, callable $callback): string
|
||||
public static function performWithEscapedBlocks(string $text, string $regex, callable $callback)
|
||||
{
|
||||
// Enables nested use
|
||||
$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)
|
||||
{
|
||||
$result = @$xpath->evaluate($element, $context);
|
||||
$result = $xpath->evaluate($element, $context);
|
||||
if (!is_object($result)) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -23,8 +23,12 @@ namespace Friendica\Worker;
|
|||
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
|
||||
class APDelivery
|
||||
{
|
||||
/**
|
||||
|
@ -42,44 +46,65 @@ class APDelivery
|
|||
public static function execute(string $cmd, int $item_id, string $inbox, int $uid, array $receivers = [], int $uri_id = 0)
|
||||
{
|
||||
if (ActivityPub\Transmitter::archivedInbox($inbox)) {
|
||||
Logger::info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
||||
if (empty($uri_id) && !empty($item_id)) {
|
||||
Logger::info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uid' => $uid]);
|
||||
if (in_array($cmd, [Delivery::POST])) {
|
||||
$item = Post::selectFirst(['uri-id'], ['id' => $item_id]);
|
||||
$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);
|
||||
Post\DeliveryData::incrementQueueFailed($item['uri-id'] ?? 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::debug('Invoked', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
||||
Logger::info('Invoked', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $item_id, 'uri-id' => $uri_id, 'uid' => $uid]);
|
||||
|
||||
if (empty($uri_id)) {
|
||||
$result = ActivityPub\Delivery::deliver($inbox);
|
||||
$success = $result['success'];
|
||||
$drop = false;
|
||||
$uri_ids = $result['uri_ids'];
|
||||
$success = true;
|
||||
|
||||
if ($cmd == Delivery::MAIL) {
|
||||
$data = ActivityPub\Transmitter::createActivityFromMail($item_id);
|
||||
if (!empty($data)) {
|
||||
$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 {
|
||||
$result = ActivityPub\Delivery::deliverToInbox($cmd, $item_id, $inbox, $uid, $receivers, $uri_id);
|
||||
$success = $result['success'];
|
||||
$drop = $result['drop'];
|
||||
$uri_ids = [$uri_id];
|
||||
$data = ActivityPub\Transmitter::createCachedActivityFromItem($item_id);
|
||||
if (!empty($data)) {
|
||||
$success = HTTPSignature::transmit($data, $inbox, $uid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$drop && !$success && !Worker::defer() && !empty($uri_ids)) {
|
||||
foreach ($uri_ids as $uri_id) {
|
||||
Post\Delivery::remove($uri_id, $inbox);
|
||||
Post\DeliveryData::incrementQueueFailed($uri_id);
|
||||
$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);
|
||||
}
|
||||
|
||||
if (!$success && !Worker::defer() && in_array($cmd, [Delivery::POST])) {
|
||||
Post\DeliveryData::incrementQueueFailed($uri_id);
|
||||
} elseif ($success && in_array($cmd, [Delivery::POST])) {
|
||||
Post\DeliveryData::incrementQueueDone($uri_id, Post\DeliveryData::ACTIVITYPUB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,10 +21,8 @@
|
|||
|
||||
namespace Friendica\Worker;
|
||||
|
||||
use Friendica\DI;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Network\HTTPException\NotFoundException;
|
||||
|
||||
class AddContact
|
||||
{
|
||||
|
@ -35,22 +33,14 @@ class AddContact
|
|||
*/
|
||||
public static function execute(int $uid, string $url)
|
||||
{
|
||||
try {
|
||||
if ($uid == 0) {
|
||||
// Adding public contact
|
||||
$result = Contact::getIdForURL($url);
|
||||
DI::logger()->info('Added public contact', ['url' => $url, 'result' => $result]);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Contact::createFromProbeForUser($uid, $url);
|
||||
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]);
|
||||
if ($uid == 0) {
|
||||
// Adding public contact
|
||||
$result = Contact::getIdForURL($url);
|
||||
Logger::info('Added public contact', ['url' => $url, 'result' => $result]);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Contact::createFromProbeForUser($uid, $url);
|
||||
Logger::info('Added contact', ['uid' => $uid, 'url' => $url, 'result' => $result]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,19 +21,31 @@
|
|||
|
||||
namespace Friendica\Worker;
|
||||
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
||||
class FetchFeaturedPosts
|
||||
/**
|
||||
* Delete all done workerqueue entries
|
||||
*/
|
||||
class CleanWorkerQueue
|
||||
{
|
||||
/**
|
||||
* Fetch featured posts from a contact with the given URL
|
||||
* @param string $url Contact URL
|
||||
*/
|
||||
public static function execute(string $url)
|
||||
public static function execute()
|
||||
{
|
||||
Logger::info('Start fetching featured posts', ['url' => $url]);
|
||||
ActivityPub\Processor::fetchFeaturedPosts($url);
|
||||
Logger::info('Finished fetching featured posts', ['url' => $url]);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user