From 3958739d605baaabc78dd7c4ca97e23678c028a3 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Tue, 21 Feb 2023 13:53:16 -0500 Subject: [PATCH 01/41] Add to Mastodon Status/Context filter for ignored and blocked user's comments --- src/Module/Api/Mastodon/Statuses/Context.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Module/Api/Mastodon/Statuses/Context.php b/src/Module/Api/Mastodon/Statuses/Context.php index 76677de4ad..f15f71744c 100644 --- a/src/Module/Api/Mastodon/Statuses/Context.php +++ b/src/Module/Api/Mastodon/Statuses/Context.php @@ -73,6 +73,13 @@ class Context extends BaseApi $condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $request['min_id']]); $params['order'] = ['uri-id']; } + + if (!empty($uid)) { + $condition = DBA::mergeConditions( + $condition, + ["NOT `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND (`blocked` OR `ignored`))", $uid] + ); + } $posts = Post::selectPosts(['uri-id', 'thr-parent-id'], $condition, $params); while ($post = Post::fetch($posts)) { From b06a3e877e9b542fa911d5da5cb4ad80b9ca8020 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 11:45:42 -0500 Subject: [PATCH 02/41] Add show_all option to Mastodon status context to show ignored and blocked statuses --- src/Module/Api/Mastodon/Statuses/Context.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Module/Api/Mastodon/Statuses/Context.php b/src/Module/Api/Mastodon/Statuses/Context.php index f15f71744c..f683b01751 100644 --- a/src/Module/Api/Mastodon/Statuses/Context.php +++ b/src/Module/Api/Mastodon/Statuses/Context.php @@ -45,10 +45,11 @@ class Context extends BaseApi } $request = $this->getRequest([ - 'max_id' => 0, // Return results older than this id - 'since_id' => 0, // Return results newer than this id - 'min_id' => 0, // Return results immediately newer than this id - 'limit' => 40, // Maximum number of results to return. Defaults to 40. + 'max_id' => 0, // Return results older than this id + 'since_id' => 0, // Return results newer than this id + 'min_id' => 0, // Return results immediately newer than this id + 'limit' => 40, // Maximum number of results to return. Defaults to 40. + 'show_all' => false, // shows posts for all users including blocked and ignored users ], $request); $id = $this->parameters['id']; @@ -74,7 +75,7 @@ class Context extends BaseApi $params['order'] = ['uri-id']; } - if (!empty($uid)) { + if (!empty($uid) && !$request['show_all']) { $condition = DBA::mergeConditions( $condition, ["NOT `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND (`blocked` OR `ignored`))", $uid] From 4a00731aa4433903a2ee38e9de67f0796f6dfeca Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 12:07:57 -0500 Subject: [PATCH 03/41] Breakup public/user ID query results so can always query blocked status by CID --- src/Factory/Api/Mastodon/Relationship.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Factory/Api/Mastodon/Relationship.php b/src/Factory/Api/Mastodon/Relationship.php index 3c9dca3024..f1ca4a1f9e 100644 --- a/src/Factory/Api/Mastodon/Relationship.php +++ b/src/Factory/Api/Mastodon/Relationship.php @@ -38,14 +38,14 @@ class Relationship extends BaseFactory public function createFromContactId(int $contactId, int $uid): RelationshipEntity { $cdata = Contact::getPublicAndUserContactID($contactId, $uid); - if (!empty($cdata)) { - $cid = $cdata['user']; - $pcid = $cdata['public']; - } else { - $pcid = $cid = $contactId; - } + $pcid = !empty($cdata['public']) ? $cdata['public'] : $contactId; + $cid = !empty($cdata['user']) ? $cdata['user'] : $contactId; - return new RelationshipEntity($pcid, Contact::getById($cid), - Contact\User::isBlocked($cid, $uid), Contact\User::isIgnored($cid, $uid)); + return new RelationshipEntity( + $pcid, + Contact::getById($cid), + Contact\User::isBlocked($cid, $uid), + Contact\User::isIgnored($cid, $uid) + ); } } From 95d615ec1c3e6bd1eb0bb023c8137ab8acd158d5 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 20:47:55 -0500 Subject: [PATCH 04/41] Add new Mastodon InstanceV2 to implemented list and fix doc links --- doc/API-Mastodon.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 03d6e3f26e..dc068202fa 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -73,8 +73,8 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - `:id` is a follow request ID, not a regular account id - Returns a [Relationship](https://docs.joinmastodon.org/entities/relationship) object. -- [`GET /api/v1/followed_tags'](https://docs.joinmastodon.org/methods/followed_tags/) -- [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance) +- [`GET /api/v1/followed_tags`](https://docs.joinmastodon.org/methods/followed_tags/) +- [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance/#v1) - `GET /api/v1/instance/rules` Undocumented, returns Terms of Service - [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains) - [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/) @@ -139,6 +139,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/trends/links`](https://github.com/mastodon/mastodon/pull/16917) - [`GET /api/v1/trends/statuses`](https://docs.joinmastodon.org/methods/trends/#statuses) - [`GET /api/v1/trends/tags`](https://docs.joinmastodon.org/methods/trends/#tags) +- [`GET /api/v2/instance`](https://docs.joinmastodon.org/methods/instance/#v2) - [`GET /api/v2/search`](https://docs.joinmastodon.org/methods/search/) From e28a0ed59c39bc3b4884318fd65b86fead804532 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 20:55:52 -0500 Subject: [PATCH 05/41] Add Dislike Friendica API documentation --- doc/API-Friendica.md | 121 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index 8460fd4ab6..4e640c2889 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -715,6 +715,127 @@ General description of profile data in API returns: --- +### POST api/friendica/statuses/:id/dislike + +Marks the given status as disliked by this user + +#### Path Parameter + +* `id`: the status ID that is being marked + +#### Return values + +A Mastodon [Status Entity](https://docs.joinmastodon.org/entities/Status/) + +#### Example: +`https:///api/friendica/statuses/341/dislike` + +```json +{ + "id": "341", + "created_at": "2023-02-23T01:50:00.000Z", + "in_reply_to_id": null, + "in_reply_to_status": null, + "in_reply_to_account_id": null, + "sensitive": false, + "spoiler_text": "", + "visibility": "public", + "language": "en", + ... + "account": { + "id": "8", + "username": "testuser2", + ... + }, + "media_attachments": [], + "mentions": [], + "tags": [], + "emojis": [], + "card": null, + "poll": null, + "friendica": { + "title": "", + "dislikes_count": 1 + } +} +``` + + +### GET api/friendica/statuses/:id/disliked_by + +Returns the list of accounts that have disliked the status as known by the current server + +#### Path Parameter + +* `id`: the status ID that is being marked + +#### Return values + +A list of [Mastodon Account](https://docs.joinmastodon.org/entities/Account/) objects +in the body and next/previous link headers in the header + +#### Example: +`https:///api/friendica/statuses/341/disliked_by` + +```json +[ + { + "id": "6", + "username": "testuser1", + ... + } +] +``` + + + +### POST api/friendica/statuses/:id/undislike + +Removes the dislike mark (if it exists) on this status for this user + +#### Path Parameter + +* `id`: the status ID that is being marked + +#### Return values + +A Mastodon [Status Entity](https://docs.joinmastodon.org/entities/Status/) + +#### Example: +`https:///api/friendica/statuses/341/dislike` + +```json +{ + "id": "341", + "created_at": "2023-02-23T01:50:00.000Z", + "in_reply_to_id": null, + "in_reply_to_status": null, + "in_reply_to_account_id": null, + "sensitive": false, + "spoiler_text": "", + "visibility": "public", + "language": "en", + ... + "account": { + "id": "8", + "username": "testuser2", + ... + }, + "media_attachments": [], + "mentions": [], + "tags": [], + "emojis": [], + "card": null, + "poll": null, + "friendica": { + "title": "", + "dislikes_count": 0 + } +} +``` + +--- + ## Deprecated endpoints - POST api/statuses/mediap From dacda1b3647c366cba91a065ebe6264ae968e68f Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 21:31:07 -0500 Subject: [PATCH 06/41] Add changes to the Photo entries in Friendica API endpoint --- doc/API-Entities.md | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/doc/API-Entities.md b/doc/API-Entities.md index 23fc2cec48..6c3e94a39b 100644 --- a/doc/API-Entities.md +++ b/doc/API-Entities.md @@ -908,6 +908,13 @@ Identical to [the Twitter Media Object](https://developer.twitter.com/en/docs/tw Resource ID (32 hex chars) + +media-id +String (Integer) +ID used for attaching to a Mastodon Post Status + + + created String (Date) @@ -1001,6 +1008,14 @@ Mutually exclusive with data datasize. + +scales +Array of Photo Scales + +List of Scale objects listing the id, scale, link, etc. of each scale + + + datasize Integer @@ -1040,6 +1055,58 @@ Mutually exclusive with link. +## Photo Scale + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeTypeNullable
idString (Integer)Row ID of this photo scale
scaleIntegerScale number
linkString (URL)URL to this scale's image
heightIntegerImage height in pixels
widthIntegerImage width in pixels
sizeIntegerImage size in bytes
+ + ## Photo List Item From 467193c661cdb273151831efca0079503216e177 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 21:49:56 -0500 Subject: [PATCH 07/41] Add Mastodon Status Entity Friendica extensions (minus quote) - I can't figure out how to get the quote one to populate, check if still relevant --- doc/API-Mastodon.md | 60 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index dc068202fa..fe2e77f103 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -29,7 +29,65 @@ For supported apps please have a look at the [FAQ](help/FAQ#clients) ## Entities -These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). +These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). With some additional extensions listed below. + +### Status Entities +Extensions to the [Mastodon Status Entities](https://docs.joinmastodon.org/entities/Status/) +* `in_reply_to_status`: A fully populated Mastodon Status entity for the replied to status or null it is a post rather than a response +* `friendica`: Friendica specific properties of a status including: + * `title`: The Friendica title for a post, or empty if the status is a comment + * `dislikes_count`: The number of dislikes that a status has accumulated according to the server. + +Example: +```json +{ + "id": "358", + "created_at": "2023-02-23T02:45:46.000Z", + "in_reply_to_id": "356", + "in_reply_to_status": { + "id": "356", + "created_at": "2023-02-23T02:45:35.000Z", + "in_reply_to_id": null, + "in_reply_to_status": null, + "in_reply_to_account_id": null, + ... + "content": "A post from testuser1", + ... + "account": { + "id": "6", + "username": "testuser1", + "acct": "testuser1", + "display_name": "testuser1", + ... + }, + ... + "friendica": { + "title": "", + "dislikes_count": 0 + } + }, + "in_reply_to_account_id": "6", + ... + "replies_count": 0, + "reblogs_count": 0, + "favourites_count": 0, + ... + "content": "A reply from testuser2", + ... + "account": { + "id": "8", + "username": "testuser2", + "acct": "testuser2", + "display_name": "testuser2", + ... + }, + ... + "friendica": { + "title": "", + "dislikes_count": 0 + } +} +``` ## Implemented endpoints From e0143618db3523574600e3a6313accdda10e6b46 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 22:07:27 -0500 Subject: [PATCH 08/41] Add Mastodon API endpoint extension information --- doc/API-Mastodon.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index fe2e77f103..932446b198 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -150,6 +150,10 @@ Example: - [`PUT /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/) - [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/) - [`GET /api/v1/notifications`](https://docs.joinmastodon.org/methods/notifications/) + - Additional field `include_all` to return read and unread statuses, defaults to `false` + - Additional field `summary` returns a count of all of the statuses that match the type filter + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Does not support the `type` field, which is the mirror image of the supported `exclude_types` field - [`GET /api/v1/notifications/:id`](https://docs.joinmastodon.org/methods/notifications/) - [`POST /api/v1/notifications/clear`](https://docs.joinmastodon.org/methods/notifications/) - [`POST /api/v1/notifications/:id/dismiss`](https://docs.joinmastodon.org/methods/notifications/) @@ -166,9 +170,15 @@ Example: - [`GET /api/v1/search`](https://docs.joinmastodon.org/methods/search/) - [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#create) - Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a group id. + - Additional field `quote_id` for the post that is being quote reshared + - Additional fields `friendica` for Friendica specific parameters: + - `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`. - [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/#get) - [`DELETE /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/#delete) - [`GET /api/v1/statuses/:id/context`](https://docs.joinmastodon.org/methods/statuses/#context) + - Additional support for paging using `min_id`, `max_id`, `since_id` parameters + - Additional support for previous/next Link Headers to support paging + - Additional flag `show_all` to allow including posts from blocked and ignored/muted users, defaults to `false` - [`GET /api/v1/statuses/:id/reblogged_by`](https://docs.joinmastodon.org/methods/statuses/#reblogged_by) - [`GET /api/v1/statuses/:id/favourited_by`](https://docs.joinmastodon.org/methods/statuses/#favourited_by) - [`POST /api/v1/statuses/:id/favourite`](https://docs.joinmastodon.org/methods/statuses/#favourite) @@ -197,6 +207,7 @@ Example: - [`GET /api/v1/trends/links`](https://github.com/mastodon/mastodon/pull/16917) - [`GET /api/v1/trends/statuses`](https://docs.joinmastodon.org/methods/trends/#statuses) - [`GET /api/v1/trends/tags`](https://docs.joinmastodon.org/methods/trends/#tags) + - Additional field `friendica_local` to return local trending tags instead of global tags, defaults to `false` - [`GET /api/v2/instance`](https://docs.joinmastodon.org/methods/instance/#v2) - [`GET /api/v2/search`](https://docs.joinmastodon.org/methods/search/) From a8661f1effa85c30d83624b0b0d1d750310c7383 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 22:20:33 -0500 Subject: [PATCH 09/41] Add Mastodon Notification Entity Extensions documentation --- doc/API-Mastodon.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 932446b198..94068dab64 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -31,6 +31,10 @@ For supported apps please have a look at the [FAQ](help/FAQ#clients) These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). With some additional extensions listed below. +### Notification Entities +Extensions to the [Mastodon Notification Entities](https://docs.joinmastodon.org/entities/Notification/) +* `dismissed`: whether the object has been dismissed or not + ### Status Entities Extensions to the [Mastodon Status Entities](https://docs.joinmastodon.org/entities/Status/) * `in_reply_to_status`: A fully populated Mastodon Status entity for the replied to status or null it is a post rather than a response From d64ae9bef2a93591020b15014a4918c20780e5f8 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 22:35:24 -0500 Subject: [PATCH 10/41] Add new Friendica Photo Album GET endpoints to documentation --- doc/API-Entities.md | 34 +++++++++++++++++ doc/API-Friendica.md | 88 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/doc/API-Entities.md b/doc/API-Entities.md index 6c3e94a39b..0242f4ab0a 100644 --- a/doc/API-Entities.md +++ b/doc/API-Entities.md @@ -1170,6 +1170,40 @@ Mutually exclusive with link.
+ +## Photo Album + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AttributeTypeDescription
nameStringThe name of the photo album
createdString (Date)The creation date of the album. Format YYYY-MM-DD HH:MM:SS
countIntegerThe number of images in the album
+ ## Private message diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index 4e640c2889..ea11c793a8 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -665,8 +665,8 @@ On success: ```json { - "result": "updated", - "message":"album 'abc' with all containing photos has been renamed to 'xyz'." + "result": "updated", + "message":"album 'abc' with all containing photos has been renamed to 'xyz'." } ``` @@ -676,8 +676,92 @@ On error: * 400 BADREQUEST: "no albumname specified", "no new albumname specified", "album not available" * 500 INTERNALSERVERERROR: "unknown error - updating in database failed" +### GET api/friendica/photoalbums + +Get a list of photo albums for the user + +#### Parameters + +None +#### Return values + +On success a list of photo album objects: + +```json +[ + { + "name": "Wall Photos", + "created": "2023-01-22 02:03:19", + "count": 4 + }, + { + "name": "Profile photos", + "created": "2022-11-20 14:40:06", + "count": 1 + } +] +``` + +### GET api/friendica/photoalbum + +Get a list of images in a photo album +#### Parameters + +* `album` (Required): name of the album to be deleted +* `limit` (Optional): Maximum number of items to get, defaults to 50, max 500 +* `offset`(Optional): Offset in results to page through total items, defaults to 0 +* `latest_first` (Optional): Reverse the order so the most recent images are first, defaults to false + +#### Return values + +On success: + +* JSON return with the list of Photo items + +**Example:** +`https:///api/friendica/photoalbum?album=Wall Photos&limit=10&offset=2` + +```json +[ + { + "created": "2023-02-14 14:31:06", + "edited": "2023-02-14 14:31:14", + "title": "", + "desc": "", + "album": "Wall Photos", + "filename": "image.png", + "type": "image/png", + "height": 835, + "width": 693, + "datasize": 119523, + "profile": 0, + "allow_cid": "", + "deny_cid": "", + "allow_gid": "", + "deny_gid": "", + "id": "899184972463eb9b2ae3dc2580502826", + "scale": 0, + "media-id": 52, + "scales": [ + { + "id": 52, + "scale": 0, + "link": "https:///photo/899184972463eb9b2ae3dc2580502826-0.png", + "width": 693, + "height": 835, + "size": 119523 + }, + ... + ], + "thumb": "https:///photo/899184972463eb9b2ae3dc2580502826-2.png" + }, + ... +] +``` + --- + ### GET api/friendica/profile/show Returns the [Profile](help/API-Entities#Profile) data of the authenticated user. From 3389a1755305bb161957a0ca3235341b39021fea Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 22:43:32 -0500 Subject: [PATCH 11/41] Add additional and not supported fields/QPs on the Mastodon Timeline Endpoints --- doc/API-Mastodon.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 94068dab64..0383d89492 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -204,9 +204,18 @@ Example: - [`GET /api/v1/tags/:id/unfollow`](https://docs.joinmastodon.org/methods/tags/#unfollow) - [`GET /api/v1/timelines/direct`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/home`](https://docs.joinmastodon.org/methods/timelines/) + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/) + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/) + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/) + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` + - Does not support the `any[]`, `all[]`, or `none[]` query parameters - [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/) - [`GET /api/v1/trends/links`](https://github.com/mastodon/mastodon/pull/16917) - [`GET /api/v1/trends/statuses`](https://docs.joinmastodon.org/methods/trends/#statuses) From 811d57cb6d8a21c54cb76468d755012c595c35f1 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 22 Feb 2023 22:55:04 -0500 Subject: [PATCH 12/41] Add Mastodon Status Editing endpoint documentation --- doc/API-Mastodon.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 0383d89492..17c6ea8fe3 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -172,7 +172,12 @@ Example: - [`DELETE /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`GET /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`GET /api/v1/search`](https://docs.joinmastodon.org/methods/search/) +- [`PUT /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#edit) + - Does not support `polls` argument as Friendica does not have polls + - Additional fields `friendica` for Friendica specific parameters: + - `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`. - [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#create) + - Does not support `polls` argument as Friendica does not have polls - Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a group id. - Additional field `quote_id` for the post that is being quote reshared - Additional fields `friendica` for Friendica specific parameters: From 7b6cb7bcf2e07818bcb1ab6ef2c405d24ebede4c Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 23 Feb 2023 06:46:43 -0500 Subject: [PATCH 13/41] Add Mastodon Instance::V2 Friendica extension documentation --- doc/API-Mastodon.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 17c6ea8fe3..4fa41e36ca 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -31,6 +31,28 @@ For supported apps please have a look at the [FAQ](help/FAQ#clients) These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). With some additional extensions listed below. +### Instance (Version 2) Entities +Extensions to the [Mastodon Instance::V2 Entities](https://docs.joinmastodon.org/entities/Instance/) +* `friendica`: Friendica specific properties of the V2 Instance including: + * `version`: The Friendica version string + * `codename`: The Friendica version code name + * `db_version`: The database schema version number + +Example: +```json +{ + "domain": "friendicadevtest1.myportal.social", + "title": "Friendica Social Network", + "version": "2.8.0 (compatible; Friendica 2023.03-dev)", + ... + "friendica": { + "version": "2023.03-dev", + "codename": "Giant Rhubarb", + "db_version": 1516 + } +} +``` + ### Notification Entities Extensions to the [Mastodon Notification Entities](https://docs.joinmastodon.org/entities/Notification/) * `dismissed`: whether the object has been dismissed or not From b243df5125e7c78ba89c918f07b2032c2913046c Mon Sep 17 00:00:00 2001 From: Hank G Date: Thu, 23 Feb 2023 10:32:09 -0500 Subject: [PATCH 14/41] Update doc/API-Entities.md Co-authored-by: Hypolite Petovan --- doc/API-Entities.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/API-Entities.md b/doc/API-Entities.md index 0242f4ab0a..473f95f9dc 100644 --- a/doc/API-Entities.md +++ b/doc/API-Entities.md @@ -911,7 +911,7 @@ Identical to [the Twitter Media Object](https://developer.twitter.com/en/docs/tw - + From 65dedb7a7eea294484143976c983255d573f4e1e Mon Sep 17 00:00:00 2001 From: Hank G Date: Thu, 23 Feb 2023 20:40:43 -0500 Subject: [PATCH 15/41] Apply suggestions from code review Changes suggested by @MrPetovan Co-authored-by: Hypolite Petovan --- doc/API-Entities.md | 2 +- doc/API-Mastodon.md | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/API-Entities.md b/doc/API-Entities.md index 473f95f9dc..213b2494f1 100644 --- a/doc/API-Entities.md +++ b/doc/API-Entities.md @@ -1012,7 +1012,7 @@ Mutually exclusive with data datasize. diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 4fa41e36ca..7496dc243d 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -29,12 +29,13 @@ For supported apps please have a look at the [FAQ](help/FAQ#clients) ## Entities -These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). With some additional extensions listed below. +These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/entities/). +With some additional extensions listed below. ### Instance (Version 2) Entities Extensions to the [Mastodon Instance::V2 Entities](https://docs.joinmastodon.org/entities/Instance/) * `friendica`: Friendica specific properties of the V2 Instance including: - * `version`: The Friendica version string + * `version`: The Friendica version string * `codename`: The Friendica version code name * `db_version`: The database schema version number @@ -176,7 +177,7 @@ Example: - [`PUT /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/) - [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/) - [`GET /api/v1/notifications`](https://docs.joinmastodon.org/methods/notifications/) - - Additional field `include_all` to return read and unread statuses, defaults to `false` + - Additional field `include_all` to return read and unread statuses, defaults to `false` - Additional field `summary` returns a count of all of the statuses that match the type filter - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - Does not support the `type` field, which is the mirror image of the supported `exclude_types` field @@ -196,10 +197,10 @@ Example: - [`GET /api/v1/search`](https://docs.joinmastodon.org/methods/search/) - [`PUT /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#edit) - Does not support `polls` argument as Friendica does not have polls - - Additional fields `friendica` for Friendica specific parameters: - - `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`. + - Additional fields `friendica` for Friendica specific parameters: + - `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`. - [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#create) - - Does not support `polls` argument as Friendica does not have polls + - Does not support `polls` argument as Friendica does not have polls - Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a group id. - Additional field `quote_id` for the post that is being quote reshared - Additional fields `friendica` for Friendica specific parameters: @@ -231,7 +232,7 @@ Example: - [`GET /api/v1/tags/:id/unfollow`](https://docs.joinmastodon.org/methods/tags/#unfollow) - [`GET /api/v1/timelines/direct`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/home`](https://docs.joinmastodon.org/methods/timelines/) - - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/) - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` @@ -240,14 +241,14 @@ Example: - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/) - - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - Does not support the `any[]`, `all[]`, or `none[]` query parameters - [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/) - [`GET /api/v1/trends/links`](https://github.com/mastodon/mastodon/pull/16917) - [`GET /api/v1/trends/statuses`](https://docs.joinmastodon.org/methods/trends/#statuses) - [`GET /api/v1/trends/tags`](https://docs.joinmastodon.org/methods/trends/#tags) - - Additional field `friendica_local` to return local trending tags instead of global tags, defaults to `false` + - Additional field `friendica_local` to return local trending tags instead of global tags, defaults to `false` - [`GET /api/v2/instance`](https://docs.joinmastodon.org/methods/instance/#v2) - [`GET /api/v2/search`](https://docs.joinmastodon.org/methods/search/) From 671fc61ef4da059b6aacdc3605f7760ca652bb05 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Thu, 23 Feb 2023 20:52:16 -0500 Subject: [PATCH 16/41] Make indentation consistent --- doc/API-Mastodon.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 7496dc243d..a78bfc261b 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -235,11 +235,11 @@ Example: - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/) - - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/) - - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` + - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` + - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` - [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/) - Additional field `with_muted` Pleroma extension to return notifications from muted users, defaults to `false` - Additional field `exclude_replies` to only return post statuses not replies/comments, defaults to `false` From 5e8acbf5342fe2fa9610b5ec978cdd9cee8a5e68 Mon Sep 17 00:00:00 2001 From: Hank G Date: Fri, 24 Feb 2023 08:46:52 -0500 Subject: [PATCH 17/41] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- doc/API-Friendica.md | 78 ++++++++++++++++++++++---------------------- doc/API-Mastodon.md | 54 +++++++++++++++--------------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index ea11c793a8..052754e048 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -724,36 +724,36 @@ On success: ```json [ { - "created": "2023-02-14 14:31:06", - "edited": "2023-02-14 14:31:14", - "title": "", - "desc": "", - "album": "Wall Photos", - "filename": "image.png", - "type": "image/png", - "height": 835, - "width": 693, - "datasize": 119523, - "profile": 0, - "allow_cid": "", - "deny_cid": "", - "allow_gid": "", - "deny_gid": "", - "id": "899184972463eb9b2ae3dc2580502826", - "scale": 0, - "media-id": 52, - "scales": [ - { - "id": 52, - "scale": 0, - "link": "https:///photo/899184972463eb9b2ae3dc2580502826-0.png", - "width": 693, - "height": 835, - "size": 119523 - }, - ... - ], - "thumb": "https:///photo/899184972463eb9b2ae3dc2580502826-2.png" + "created": "2023-02-14 14:31:06", + "edited": "2023-02-14 14:31:14", + "title": "", + "desc": "", + "album": "Wall Photos", + "filename": "image.png", + "type": "image/png", + "height": 835, + "width": 693, + "datasize": 119523, + "profile": 0, + "allow_cid": "", + "deny_cid": "", + "allow_gid": "", + "deny_gid": "", + "id": "899184972463eb9b2ae3dc2580502826", + "scale": 0, + "media-id": 52, + "scales": [ + { + "id": 52, + "scale": 0, + "link": "https:///photo/899184972463eb9b2ae3dc2580502826-0.png", + "width": 693, + "height": 835, + "size": 119523 + }, + ... + ], + "thumb": "https:///photo/899184972463eb9b2ae3dc2580502826-2.png" }, ... ] @@ -825,11 +825,11 @@ A Mastodon [Status Entity](https://docs.joinmastodon.org/entities/Status/) "spoiler_text": "", "visibility": "public", "language": "en", - ... + ... "account": { "id": "8", "username": "testuser2", - ... + ... }, "media_attachments": [], "mentions": [], @@ -863,11 +863,11 @@ in the body and next/previous link headers in the header ```json [ - { - "id": "6", - "username": "testuser1", - ... - } + { + "id": "6", + "username": "testuser1", + ... + } ] ``` @@ -899,11 +899,11 @@ A Mastodon [Status Entity](https://docs.joinmastodon.org/entities/Status/) "spoiler_text": "", "visibility": "public", "language": "en", - ... + ... "account": { "id": "8", "username": "testuser2", - ... + ... }, "media_attachments": [], "mentions": [], diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index a78bfc261b..0d7f921a27 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -72,26 +72,26 @@ Example: "created_at": "2023-02-23T02:45:46.000Z", "in_reply_to_id": "356", "in_reply_to_status": { - "id": "356", - "created_at": "2023-02-23T02:45:35.000Z", - "in_reply_to_id": null, - "in_reply_to_status": null, - "in_reply_to_account_id": null, - ... - "content": "A post from testuser1", - ... - "account": { - "id": "6", - "username": "testuser1", - "acct": "testuser1", - "display_name": "testuser1", - ... - }, - ... - "friendica": { - "title": "", - "dislikes_count": 0 - } + "id": "356", + "created_at": "2023-02-23T02:45:35.000Z", + "in_reply_to_id": null, + "in_reply_to_status": null, + "in_reply_to_account_id": null, + ... + "content": "A post from testuser1", + ... + "account": { + "id": "6", + "username": "testuser1", + "acct": "testuser1", + "display_name": "testuser1", + ... + }, + ... + "friendica": { + "title": "", + "dislikes_count": 0 + } }, "in_reply_to_account_id": "6", ... @@ -102,16 +102,16 @@ Example: "content": "A reply from testuser2", ... "account": { - "id": "8", - "username": "testuser2", - "acct": "testuser2", - "display_name": "testuser2", - ... + "id": "8", + "username": "testuser2", + "acct": "testuser2", + "display_name": "testuser2", + ... }, ... "friendica": { - "title": "", - "dislikes_count": 0 + "title": "", + "dislikes_count": 0 } } ``` From c0741be417cbf0fe5858d9da7b7b0a0f9435055b Mon Sep 17 00:00:00 2001 From: Hank G Date: Fri, 24 Feb 2023 08:47:37 -0500 Subject: [PATCH 18/41] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- doc/API-Friendica.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/API-Friendica.md b/doc/API-Friendica.md index 052754e048..a33cd53b86 100644 --- a/doc/API-Friendica.md +++ b/doc/API-Friendica.md @@ -690,14 +690,14 @@ On success a list of photo album objects: ```json [ { - "name": "Wall Photos", - "created": "2023-01-22 02:03:19", - "count": 4 + "name": "Wall Photos", + "created": "2023-01-22 02:03:19", + "count": 4 }, { - "name": "Profile photos", - "created": "2022-11-20 14:40:06", - "count": 1 + "name": "Profile photos", + "created": "2022-11-20 14:40:06", + "count": 1 } ] ``` From 751e335b4b1f56c1645878155619c980fa7c3e87 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sat, 25 Feb 2023 14:23:59 -0500 Subject: [PATCH 19/41] Add delivery status data to Mastodon Status Friendica Extension --- src/Factory/Api/Mastodon/Status.php | 16 +++++- src/Object/Api/Mastodon/Status.php | 4 +- .../Mastodon/Status/FriendicaDeliveryData.php | 55 +++++++++++++++++++ .../Mastodon/Status/FriendicaExtension.php | 8 ++- 4 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index 788841a9bc..c1065da454 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -33,6 +33,8 @@ use Friendica\Model\Post; use Friendica\Model\Tag as TagModel; use Friendica\Model\Verb; use Friendica\Network\HTTPException; +use Friendica\Object\Api\Mastodon\Status\FriendicaDeliveryData; +use Friendica\Object\Api\Mastodon\Status\FriendicaExtension; use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use ImagickException; @@ -97,7 +99,8 @@ class Status extends BaseFactory public function createFromUriId(int $uriId, int $uid = 0, bool $display_quote = false, bool $reblog = true, bool $in_reply_status = true): \Friendica\Object\Api\Mastodon\Status { $fields = ['uri-id', 'uid', 'author-id', 'causer-id', 'author-uri-id', 'author-link', 'causer-uri-id', 'post-reason', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id', - 'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'featured', 'has-media', 'quote-uri-id']; + 'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'featured', 'has-media', 'quote-uri-id', + 'delivery_queue_count', 'delivery_queue_done','delivery_queue_failed']; $item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]); if (!$item) { $mail = DBA::selectFirst('mail', ['id'], ['uri-id' => $uriId, 'uid' => $uid]); @@ -285,7 +288,13 @@ class Status extends BaseFactory $in_reply = []; } - return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $quote, $poll); + $queue_count = $item['delivery_queue_count']; + $queue_done = $item['delivery_queue_done']; + $queue_failed = $item['delivery_queue_failed']; + $delivery_data = new FriendicaDeliveryData($queue_count, $queue_done, $queue_failed); + $friendica = new FriendicaExtension($item['title'], $counts->dislikes, $delivery_data); + + return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica, $quote, $poll); } /** @@ -349,7 +358,8 @@ class Status extends BaseFactory $attachments = []; $in_reply = []; $reshare = []; + $friendica = new FriendicaExtension('', 0, new FriendicaDeliveryData(0, 0, 0)); - return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare); + return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica); } } diff --git a/src/Object/Api/Mastodon/Status.php b/src/Object/Api/Mastodon/Status.php index 30f554bdf5..2e7a0877d4 100644 --- a/src/Object/Api/Mastodon/Status.php +++ b/src/Object/Api/Mastodon/Status.php @@ -105,7 +105,7 @@ class Status extends BaseDataTransferObject * @param array $item * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, array $quote = null, array $poll = null) + public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, FriendicaExtension $friendica, array $quote = null, array $poll = null) { $this->id = (string)$item['uri-id']; $this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::JSON); @@ -151,7 +151,7 @@ class Status extends BaseDataTransferObject $this->emojis = []; $this->card = $card->toArray() ?: null; $this->poll = $poll; - $this->friendica = new FriendicaExtension($item['title'], $counts->dislikes); + $this->friendica = $friendica; } /** diff --git a/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php b/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php new file mode 100644 index 0000000000..2d9b151bee --- /dev/null +++ b/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php @@ -0,0 +1,55 @@ +. + * + */ + +namespace Friendica\Object\Api\Mastodon\Status; + +use Friendica\BaseDataTransferObject; + +/** + * Class FriendicaDeliveryData + * + * Additional fields on Mastodon Statuses for storing Friendica delivery data + * + * @see https://docs.joinmastodon.org/entities/status + */ +class FriendicaDeliveryData extends BaseDataTransferObject +{ + /** @var int */ + protected $delivery_queue_count; + + /** @var int */ + protected $delivery_queue_done; + + /** @var int */ + protected $delivery_queue_failed; + + /** + * Creates a FriendicaDeliveryData object + * + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public function __construct(int $delivery_queue_count, int $delivery_queue_done, int $delivery_queue_failed) + { + $this->delivery_queue_count = $delivery_queue_count; + $this->delivery_queue_done = $delivery_queue_done; + $this->delivery_queue_failed = $delivery_queue_failed; + } +} diff --git a/src/Object/Api/Mastodon/Status/FriendicaExtension.php b/src/Object/Api/Mastodon/Status/FriendicaExtension.php index 1db70f7312..b2853894ad 100644 --- a/src/Object/Api/Mastodon/Status/FriendicaExtension.php +++ b/src/Object/Api/Mastodon/Status/FriendicaExtension.php @@ -35,6 +35,8 @@ class FriendicaExtension extends BaseDataTransferObject /** @var string */ protected $title; + /** @var FriendicaDeliveryData */ + protected $delivery_data; /** @var int */ protected $dislikes_count; @@ -42,11 +44,13 @@ class FriendicaExtension extends BaseDataTransferObject * Creates a status count object * * @param string $title - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @param int $dislikes_count + * @param FriendicaDeliveryData $delivery_data */ - public function __construct(string $title, int $dislikes_count) + public function __construct(string $title, int $dislikes_count, FriendicaDeliveryData $delivery_data) { $this->title = $title; + $this->delivery_data = $delivery_data; $this->dislikes_count = $dislikes_count; } } From 2b513a48c796927bcb028d61a763f47e73598351 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 26 Feb 2023 14:08:33 +0000 Subject: [PATCH 20/41] Catch all errors thrown by "fetchRaw" --- src/Contact/Avatar.php | 7 +++++- src/Core/Storage/Type/ExternalResource.php | 1 + src/Model/APContact.php | 25 +++++++++++++--------- src/Model/Contact.php | 21 +++++++++++------- src/Model/Post/Link.php | 9 ++++++-- src/Module/Proxy.php | 15 ++++++++----- src/Protocol/ActivityPub/Processor.php | 7 +++++- src/Util/HTTPSignature.php | 7 +++++- 8 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/Contact/Avatar.php b/src/Contact/Avatar.php index e039a52799..4843e5d37c 100644 --- a/src/Contact/Avatar.php +++ b/src/Contact/Avatar.php @@ -73,7 +73,12 @@ class Avatar return $fields; } - $fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); + try { + $fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); + } catch (\Throwable $th) { + Logger::notice('Avatar is invalid', ['avatar' => $avatar, 'error' => $th]); + return $fields; + } $img_str = $fetchResult->getBody(); if (empty($img_str)) { diff --git a/src/Core/Storage/Type/ExternalResource.php b/src/Core/Storage/Type/ExternalResource.php index 055db0deaa..54d47e5bf4 100644 --- a/src/Core/Storage/Type/ExternalResource.php +++ b/src/Core/Storage/Type/ExternalResource.php @@ -57,6 +57,7 @@ class ExternalResource implements ICanReadFromStorage try { $fetchResult = HTTPSignature::fetchRaw($data->url, $data->uid, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); } catch (Exception $exception) { + Logger::notice('URL is invalid', ['url' => $data->url, 'error' => $exception]); throw new ReferenceStorageException(sprintf('External resource failed to get %s', $reference), $exception->getCode(), $exception); } if (!empty($fetchResult) && $fetchResult->isSuccess()) { diff --git a/src/Model/APContact.php b/src/Model/APContact.php index 215d7e317d..a429a7a5d4 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -189,17 +189,22 @@ class APContact if (empty($data)) { $local_owner = []; - $curlResult = HTTPSignature::fetchRaw($url); - $failed = empty($curlResult) || empty($curlResult->getBody()) || - (!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410)); + try { + $curlResult = HTTPSignature::fetchRaw($url); + $failed = empty($curlResult) || empty($curlResult->getBody()) || + (!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410)); + + if (!$failed) { + $data = json_decode($curlResult->getBody(), true); + $failed = empty($data) || !is_array($data); + } - if (!$failed) { - $data = json_decode($curlResult->getBody(), true); - $failed = empty($data) || !is_array($data); - } - - if (!$failed && ($curlResult->getReturnCode() == 410)) { - $data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone']; + if (!$failed && ($curlResult->getReturnCode() == 410)) { + $data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone']; + } + } catch (\Throwable $th) { + Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); + $failed = true; } if ($failed) { diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 571253f425..64509b5d22 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2216,16 +2216,21 @@ class Contact if (($contact['avatar'] != $avatar) || empty($contact['blurhash'])) { $update_fields = ['avatar' => $avatar]; if (!Network::isLocalLink($avatar)) { - $fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); + try { + $fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); - $img_str = $fetchResult->getBody(); - if (!empty($img_str)) { - $image = new Image($img_str, Images::getMimeTypeByData($img_str)); - if ($image->isValid()) { - $update_fields['blurhash'] = $image->getBlurHash(); - } else { - return; + $img_str = $fetchResult->getBody(); + if (!empty($img_str)) { + $image = new Image($img_str, Images::getMimeTypeByData($img_str)); + if ($image->isValid()) { + $update_fields['blurhash'] = $image->getBlurHash(); + } else { + return; + } } + } catch (\Throwable $th) { + Logger::notice('Error fetching avatar', ['avatar' => $avatar, 'error' => $th]); + return; } } elseif (!empty($contact['blurhash'])) { $update_fields['blurhash'] = null; diff --git a/src/Model/Post/Link.php b/src/Model/Post/Link.php index 343ad815c3..4ac8173799 100644 --- a/src/Model/Post/Link.php +++ b/src/Model/Post/Link.php @@ -125,8 +125,13 @@ class Link { $timeout = DI::config()->get('system', 'xrd_timeout'); - $curlResult = HTTPSignature::fetchRaw($url, 0, [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::ACCEPT_CONTENT => $accept]); - if (empty($curlResult) || !$curlResult->isSuccess()) { + try { + $curlResult = HTTPSignature::fetchRaw($url, 0, [HttpClientOptions::TIMEOUT => $timeout, HttpClientOptions::ACCEPT_CONTENT => $accept]); + if (empty($curlResult) || !$curlResult->isSuccess()) { + return []; + } + } catch (\Throwable $th) { + Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); return []; } $fields = ['mimetype' => $curlResult->getHeader('Content-Type')[0]]; diff --git a/src/Module/Proxy.php b/src/Module/Proxy.php index ab129a82c9..3f1e4b2b80 100644 --- a/src/Module/Proxy.php +++ b/src/Module/Proxy.php @@ -83,13 +83,18 @@ class Proxy extends BaseModule $request['url'] = str_replace(' ', '+', $request['url']); // Fetch the content with the local user - $fetchResult = HTTPSignature::fetchRaw($request['url'], DI::userSession()->getLocalUserId(), [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE], 'timeout' => 10]); - $img_str = $fetchResult->getBody(); + try { + $fetchResult = HTTPSignature::fetchRaw($request['url'], DI::userSession()->getLocalUserId(), [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE], 'timeout' => 10]); + $img_str = $fetchResult->getBody(); - if (!$fetchResult->isSuccess() || empty($img_str)) { - Logger::notice('Error fetching image', ['image' => $request['url'], 'return' => $fetchResult->getReturnCode(), 'empty' => empty($img_str)]); + if (!$fetchResult->isSuccess() || empty($img_str)) { + Logger::notice('Error fetching image', ['image' => $request['url'], 'return' => $fetchResult->getReturnCode(), 'empty' => empty($img_str)]); + self::responseError(); + // stop. + } + } catch (\Throwable $th) { + Logger::notice('Error fetching image', ['image' => $request['url'], 'error' => $th]); self::responseError(); - // stop. } Logger::debug('Got picture', ['Content-Type' => $fetchResult->getHeader('Content-Type'), 'uid' => DI::userSession()->getLocalUserId(), 'image' => $request['url']]); diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 11267cfaf5..20736504ef 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -570,7 +570,12 @@ class Processor */ public static function isActivityGone(string $url): bool { - $curlResult = HTTPSignature::fetchRaw($url, 0); + try { + $curlResult = HTTPSignature::fetchRaw($url, 0); + } catch (\Throwable $th) { + Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); + return true; + } if (Network::isUrlBlocked($url)) { return true; diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index f082fe32f7..fe623f2968 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -422,7 +422,12 @@ class HTTPSignature */ public static function fetch(string $request, int $uid): array { - $curlResult = self::fetchRaw($request, $uid); + try { + $curlResult = self::fetchRaw($request, $uid); + } catch (\Throwable $th) { + Logger::notice('Error fetching url', ['url' => $request, 'error' => $th]); + return []; + } if (empty($curlResult)) { return []; From e92b0a73f48be7200ab623054878dd5f15a12ce0 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 09:18:46 -0500 Subject: [PATCH 21/41] Update documentation for delivery status data addition to Mastodon Status --- doc/API-Mastodon.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 0d7f921a27..5fcb4782c5 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -63,6 +63,10 @@ Extensions to the [Mastodon Status Entities](https://docs.joinmastodon.org/entit * `in_reply_to_status`: A fully populated Mastodon Status entity for the replied to status or null it is a post rather than a response * `friendica`: Friendica specific properties of a status including: * `title`: The Friendica title for a post, or empty if the status is a comment + * `delivery_data`: Information about the state of federating a message from the server + * `delivery_queue_count`: Total number of remote servers that the status needs to be federated to. + * `delivery_queue_done`: Total number of remote servers that have successfully been federated to so far. + * `delivery_queue_failed`: Total number of remote servers that have we failed to federate to so far. * `dislikes_count`: The number of dislikes that a status has accumulated according to the server. Example: @@ -111,6 +115,11 @@ Example: ... "friendica": { "title": "", + "delivery_data": { + "delivery_queue_count": 10, + "delivery_queue_done": 3, + "delivery_queue_failed": 0 + }, "dislikes_count": 0 } } From 0f811abc5072d17853929ae7af5c782a5bad8075 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 09:38:22 -0500 Subject: [PATCH 22/41] Add nullability to InstanceV2 Contact type for account parameter --- src/Object/Api/Mastodon/InstanceV2/Contact.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Object/Api/Mastodon/InstanceV2/Contact.php b/src/Object/Api/Mastodon/InstanceV2/Contact.php index f48b027cf0..74a6f8e248 100644 --- a/src/Object/Api/Mastodon/InstanceV2/Contact.php +++ b/src/Object/Api/Mastodon/InstanceV2/Contact.php @@ -39,9 +39,9 @@ class Contact extends BaseDataTransferObject /** * @param string $email - * @param Account $account + * @param Account|null $account */ - public function __construct(string $email, Account $account) + public function __construct(string $email, ?Account $account) { $this->email = $email; $this->account = $account; From ea782d97fc07d48e4cc0e41b2e81649807b8e074 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 10:26:15 -0500 Subject: [PATCH 23/41] Make calls inline and return 0 if value is null --- src/Factory/Api/Mastodon/Status.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index c1065da454..db2f94d495 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -288,10 +288,7 @@ class Status extends BaseFactory $in_reply = []; } - $queue_count = $item['delivery_queue_count']; - $queue_done = $item['delivery_queue_done']; - $queue_failed = $item['delivery_queue_failed']; - $delivery_data = new FriendicaDeliveryData($queue_count, $queue_done, $queue_failed); + $delivery_data = new FriendicaDeliveryData($item['delivery_queue_count'] ?? 0, $item['delivery_queue_done'] ?? 0, $item['delivery_queue_failed'] ?? 0); $friendica = new FriendicaExtension($item['title'], $counts->dislikes, $delivery_data); return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica, $quote, $poll); From 9ab73c30efd7bfe17c88e1554eefbe2b7d7863af Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 11:27:01 -0500 Subject: [PATCH 24/41] Allow delivery stats to be null. --- src/Factory/Api/Mastodon/Status.php | 2 +- src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index db2f94d495..fed7ea4730 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -288,7 +288,7 @@ class Status extends BaseFactory $in_reply = []; } - $delivery_data = new FriendicaDeliveryData($item['delivery_queue_count'] ?? 0, $item['delivery_queue_done'] ?? 0, $item['delivery_queue_failed'] ?? 0); + $delivery_data = new FriendicaDeliveryData($item['delivery_queue_count'], $item['delivery_queue_done'], $item['delivery_queue_failed']); $friendica = new FriendicaExtension($item['title'], $counts->dislikes, $delivery_data); return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $in_reply, $reshare, $friendica, $quote, $poll); diff --git a/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php b/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php index 2d9b151bee..3da0eae57c 100644 --- a/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php +++ b/src/Object/Api/Mastodon/Status/FriendicaDeliveryData.php @@ -32,13 +32,13 @@ use Friendica\BaseDataTransferObject; */ class FriendicaDeliveryData extends BaseDataTransferObject { - /** @var int */ + /** @var int|null */ protected $delivery_queue_count; - /** @var int */ + /** @var int|null */ protected $delivery_queue_done; - /** @var int */ + /** @var int|null */ protected $delivery_queue_failed; /** @@ -46,7 +46,7 @@ class FriendicaDeliveryData extends BaseDataTransferObject * * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(int $delivery_queue_count, int $delivery_queue_done, int $delivery_queue_failed) + public function __construct(?int $delivery_queue_count, ?int $delivery_queue_done, ?int $delivery_queue_failed) { $this->delivery_queue_count = $delivery_queue_count; $this->delivery_queue_done = $delivery_queue_done; From 6e3602591d52771f7f3c8c4c0d926a1690891730 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 26 Feb 2023 22:43:45 +0000 Subject: [PATCH 25/41] Use "Exception" --- src/Contact/Avatar.php | 4 ++-- src/Factory/Api/Mastodon/Notification.php | 2 +- src/Factory/Api/Mastodon/Status.php | 12 ++++++------ src/Model/APContact.php | 4 ++-- src/Model/Contact.php | 4 ++-- src/Model/Post/Link.php | 4 ++-- src/Module/Api/Mastodon/Accounts/Statuses.php | 4 ++-- src/Module/Api/Mastodon/Bookmarks.php | 4 ++-- src/Module/Api/Mastodon/Favourited.php | 4 ++-- src/Module/Api/Mastodon/Lists/Accounts.php | 2 +- src/Module/Api/Mastodon/Search.php | 4 ++-- src/Module/Api/Mastodon/Timelines/Home.php | 4 ++-- src/Module/Api/Mastodon/Timelines/ListTimeline.php | 4 ++-- src/Module/Api/Mastodon/Timelines/PublicTimeline.php | 4 ++-- src/Module/Api/Mastodon/Timelines/Tag.php | 4 ++-- src/Module/Api/Mastodon/Trends/Statuses.php | 4 ++-- src/Module/Proxy.php | 4 ++-- src/Object/Image.php | 2 +- src/Protocol/ActivityPub/Processor.php | 4 ++-- src/Util/HTTPSignature.php | 4 ++-- 20 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/Contact/Avatar.php b/src/Contact/Avatar.php index 4843e5d37c..299c263994 100644 --- a/src/Contact/Avatar.php +++ b/src/Contact/Avatar.php @@ -75,8 +75,8 @@ class Avatar try { $fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); - } catch (\Throwable $th) { - Logger::notice('Avatar is invalid', ['avatar' => $avatar, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Avatar is invalid', ['avatar' => $avatar, 'exception' => $exception]); return $fields; } diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php index 52241e1fc8..e6e9f4d094 100644 --- a/src/Factory/Api/Mastodon/Notification.php +++ b/src/Factory/Api/Mastodon/Notification.php @@ -64,7 +64,7 @@ class Notification extends BaseFactory if ($Notification->targetUriId) { try { $status = $this->mstdnStatusFactory->createFromUriId($Notification->targetUriId, $Notification->uid, $display_quotes); - } catch (\Throwable $th) { + } catch (\Exception $exception) { $status = null; } } else { diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index 788841a9bc..f653107d40 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -266,8 +266,8 @@ class Status extends BaseFactory if ($is_reshare) { try { $reshare = $this->createFromUriId($uriId, $uid, $display_quote, false, false)->toArray(); - } catch (\Throwable $th) { - Logger::info('Reshare not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Reshare not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); $reshare = []; } } else { @@ -277,8 +277,8 @@ class Status extends BaseFactory if ($in_reply_status && ($item['gravity'] == Item::GRAVITY_COMMENT)) { try { $in_reply = $this->createFromUriId($item['thr-parent-id'], $uid, $display_quote, false, false)->toArray(); - } catch (\Throwable $th) { - Logger::info('Reply post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Reply post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); $in_reply = []; } } else { @@ -309,8 +309,8 @@ class Status extends BaseFactory if (!empty($quote_id)) { try { $quote = $this->createFromUriId($quote_id, $uid, false, false, false)->toArray(); - } catch (\Throwable $th) { - Logger::info('Quote not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Quote not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); $quote = []; } } else { diff --git a/src/Model/APContact.php b/src/Model/APContact.php index a429a7a5d4..20457b46e8 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -202,8 +202,8 @@ class APContact if (!$failed && ($curlResult->getReturnCode() == 410)) { $data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone']; } - } catch (\Throwable $th) { - Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]); $failed = true; } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 64509b5d22..f35808f050 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2228,8 +2228,8 @@ class Contact return; } } - } catch (\Throwable $th) { - Logger::notice('Error fetching avatar', ['avatar' => $avatar, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching avatar', ['avatar' => $avatar, 'exception' => $exception]); return; } } elseif (!empty($contact['blurhash'])) { diff --git a/src/Model/Post/Link.php b/src/Model/Post/Link.php index 4ac8173799..dc77979511 100644 --- a/src/Model/Post/Link.php +++ b/src/Model/Post/Link.php @@ -130,8 +130,8 @@ class Link if (empty($curlResult) || !$curlResult->isSuccess()) { return []; } - } catch (\Throwable $th) { - Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]); return []; } $fields = ['mimetype' => $curlResult->getHeader('Content-Type')[0]]; diff --git a/src/Module/Api/Mastodon/Accounts/Statuses.php b/src/Module/Api/Mastodon/Accounts/Statuses.php index 067e330271..463411745c 100644 --- a/src/Module/Api/Mastodon/Accounts/Statuses.php +++ b/src/Module/Api/Mastodon/Accounts/Statuses.php @@ -120,8 +120,8 @@ class Statuses extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Bookmarks.php b/src/Module/Api/Mastodon/Bookmarks.php index 615bfc501f..59cf7f54a2 100644 --- a/src/Module/Api/Mastodon/Bookmarks.php +++ b/src/Module/Api/Mastodon/Bookmarks.php @@ -77,8 +77,8 @@ class Bookmarks extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Favourited.php b/src/Module/Api/Mastodon/Favourited.php index 93c9ef2436..7829f37d70 100644 --- a/src/Module/Api/Mastodon/Favourited.php +++ b/src/Module/Api/Mastodon/Favourited.php @@ -79,8 +79,8 @@ class Favourited extends BaseApi self::setBoundaries($item['thr-parent-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['thr-parent-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Lists/Accounts.php b/src/Module/Api/Mastodon/Lists/Accounts.php index 667cd87eab..e19dfb0314 100644 --- a/src/Module/Api/Mastodon/Lists/Accounts.php +++ b/src/Module/Api/Mastodon/Lists/Accounts.php @@ -117,7 +117,7 @@ class Accounts extends BaseApi self::setBoundaries($member['contact-id']); try { $accounts[] = DI::mstdnAccount()->createFromContactId($member['contact-id'], $uid); - } catch (\Throwable $th) { + } catch (\Exception $exception) { } } DBA::close($members); diff --git a/src/Module/Api/Mastodon/Search.php b/src/Module/Api/Mastodon/Search.php index c35e33062b..69e2627682 100644 --- a/src/Module/Api/Mastodon/Search.php +++ b/src/Module/Api/Mastodon/Search.php @@ -183,8 +183,8 @@ class Search extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Timelines/Home.php b/src/Module/Api/Mastodon/Timelines/Home.php index 5efaf4930e..7fc179297c 100644 --- a/src/Module/Api/Mastodon/Timelines/Home.php +++ b/src/Module/Api/Mastodon/Timelines/Home.php @@ -99,8 +99,8 @@ class Home extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Timelines/ListTimeline.php b/src/Module/Api/Mastodon/Timelines/ListTimeline.php index a8de13056e..1e9a5810c1 100644 --- a/src/Module/Api/Mastodon/Timelines/ListTimeline.php +++ b/src/Module/Api/Mastodon/Timelines/ListTimeline.php @@ -104,8 +104,8 @@ class ListTimeline extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Timelines/PublicTimeline.php b/src/Module/Api/Mastodon/Timelines/PublicTimeline.php index 968e34dbb9..31750d4a5c 100644 --- a/src/Module/Api/Mastodon/Timelines/PublicTimeline.php +++ b/src/Module/Api/Mastodon/Timelines/PublicTimeline.php @@ -99,8 +99,8 @@ class PublicTimeline extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Timelines/Tag.php b/src/Module/Api/Mastodon/Timelines/Tag.php index 64cd84366e..476c9c2b94 100644 --- a/src/Module/Api/Mastodon/Timelines/Tag.php +++ b/src/Module/Api/Mastodon/Timelines/Tag.php @@ -120,8 +120,8 @@ class Tag extends BaseApi self::setBoundaries($item['uri-id']); try { $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($items); diff --git a/src/Module/Api/Mastodon/Trends/Statuses.php b/src/Module/Api/Mastodon/Trends/Statuses.php index 884319aa48..336f671559 100644 --- a/src/Module/Api/Mastodon/Trends/Statuses.php +++ b/src/Module/Api/Mastodon/Trends/Statuses.php @@ -57,8 +57,8 @@ class Statuses extends BaseApi while ($status = Post::fetch($statuses)) { try { $trending[] = DI::mstdnStatus()->createFromUriId($status['uri-id'], $uid, $display_quotes); - } catch (\Throwable $th) { - Logger::info('Post not fetchable', ['uri-id' => $status['uri-id'], 'uid' => $uid, 'error' => $th]); + } catch (\Exception $exception) { + Logger::info('Post not fetchable', ['uri-id' => $status['uri-id'], 'uid' => $uid, 'exception' => $exception]); } } DBA::close($statuses); diff --git a/src/Module/Proxy.php b/src/Module/Proxy.php index 3f1e4b2b80..4d1e1c304b 100644 --- a/src/Module/Proxy.php +++ b/src/Module/Proxy.php @@ -92,8 +92,8 @@ class Proxy extends BaseModule self::responseError(); // stop. } - } catch (\Throwable $th) { - Logger::notice('Error fetching image', ['image' => $request['url'], 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching image', ['image' => $request['url'], 'exception' => $exception]); self::responseError(); } diff --git a/src/Object/Image.php b/src/Object/Image.php index d6c897e88d..49196a0c44 100644 --- a/src/Object/Image.php +++ b/src/Object/Image.php @@ -718,7 +718,7 @@ class Image if ($image->isImagick()) { try { $colors = $image->image->getImagePixelColor($x, $y)->getColor(); - } catch (\Throwable $th) { + } catch (\Exception $exception) { return ''; } $row[] = [$colors['r'], $colors['g'], $colors['b']]; diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 20736504ef..54acca9df3 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -572,8 +572,8 @@ class Processor { try { $curlResult = HTTPSignature::fetchRaw($url, 0); - } catch (\Throwable $th) { - Logger::notice('Error fetching url', ['url' => $url, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]); return true; } diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index fe623f2968..d48d02c20e 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -424,8 +424,8 @@ class HTTPSignature { try { $curlResult = self::fetchRaw($request, $uid); - } catch (\Throwable $th) { - Logger::notice('Error fetching url', ['url' => $request, 'error' => $th]); + } catch (\Exception $exception) { + Logger::notice('Error fetching url', ['url' => $request, 'exception' => $exception]); return []; } From 706a42ffd0bdff937a3977b2f2bf82c9e8636f7c Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 22:56:56 -0500 Subject: [PATCH 26/41] Change Mastodon Instance V2 active_monthly field to correct active_month --- src/Object/Api/Mastodon/InstanceV2/UserStats.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Object/Api/Mastodon/InstanceV2/UserStats.php b/src/Object/Api/Mastodon/InstanceV2/UserStats.php index 8f398e0d5a..34fb3a6556 100644 --- a/src/Object/Api/Mastodon/InstanceV2/UserStats.php +++ b/src/Object/Api/Mastodon/InstanceV2/UserStats.php @@ -31,13 +31,13 @@ use Friendica\BaseDataTransferObject; class UserStats extends BaseDataTransferObject { /** @var int */ - protected $active_monthly = 0; + protected $active_month = 0; /** * @param $active_monthly */ - public function __construct($active_monthly) + public function __construct($active_month) { - $this->active_monthly = $active_monthly; + $this->active_month = $active_month; } } From bb29a07efa6cfb37637589d1035f5648f273145f Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Sun, 26 Feb 2023 22:57:10 -0500 Subject: [PATCH 27/41] Change Mastodon Instance V2 active_monthly field to correct active_month --- src/Object/Api/Mastodon/InstanceV2/UserStats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Object/Api/Mastodon/InstanceV2/UserStats.php b/src/Object/Api/Mastodon/InstanceV2/UserStats.php index 34fb3a6556..e6c8634ae6 100644 --- a/src/Object/Api/Mastodon/InstanceV2/UserStats.php +++ b/src/Object/Api/Mastodon/InstanceV2/UserStats.php @@ -34,7 +34,7 @@ class UserStats extends BaseDataTransferObject protected $active_month = 0; /** - * @param $active_monthly + * @param $active_month */ public function __construct($active_month) { From bd3120d6cb3b52cf103d4e2f36f0cd93d359d184 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Feb 2023 07:02:06 +0000 Subject: [PATCH 28/41] Use non static logger call --- src/Core/Storage/Repository/StorageManager.php | 6 +++--- src/Core/Storage/Type/ExternalResource.php | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Core/Storage/Repository/StorageManager.php b/src/Core/Storage/Repository/StorageManager.php index fbe910d01d..252454665d 100644 --- a/src/Core/Storage/Repository/StorageManager.php +++ b/src/Core/Storage/Repository/StorageManager.php @@ -168,7 +168,7 @@ class StorageManager return $data['storage_config']; } catch (InternalServerErrorException $exception) { - throw new StorageException(sprintf('Failed calling hook::storage_config for backend %s', $name), $exception); + throw new StorageException(sprintf('Failed calling hook::storage_config for backend %s', $name), $exception->__toString()); } } } @@ -208,7 +208,7 @@ class StorageManager $this->backendInstances[$name] = new Type\SystemResource(); break; case Type\ExternalResource::getName(): - $this->backendInstances[$name] = new Type\ExternalResource(); + $this->backendInstances[$name] = new Type\ExternalResource($this->logger); break; default: $data = [ @@ -223,7 +223,7 @@ class StorageManager $this->backendInstances[$data['name'] ?? $name] = $data['storage']; } catch (InternalServerErrorException $exception) { - throw new StorageException(sprintf('Failed calling hook::storage_instance for backend %s', $name), $exception); + throw new StorageException(sprintf('Failed calling hook::storage_instance for backend %s', $name), $exception->__toString()); } break; } diff --git a/src/Core/Storage/Type/ExternalResource.php b/src/Core/Storage/Type/ExternalResource.php index 54d47e5bf4..181312a5bf 100644 --- a/src/Core/Storage/Type/ExternalResource.php +++ b/src/Core/Storage/Type/ExternalResource.php @@ -22,12 +22,12 @@ namespace Friendica\Core\Storage\Type; use Exception; -use Friendica\Core\Logger; use Friendica\Core\Storage\Exception\ReferenceStorageException; use Friendica\Core\Storage\Capability\ICanReadFromStorage; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Util\HTTPSignature; +use Psr\Log\LoggerInterface; /** * External resource storage class @@ -39,6 +39,14 @@ class ExternalResource implements ICanReadFromStorage { const NAME = 'ExternalResource'; + /** @var LoggerInterface */ + protected $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + /** * @inheritDoc */ @@ -57,11 +65,11 @@ class ExternalResource implements ICanReadFromStorage try { $fetchResult = HTTPSignature::fetchRaw($data->url, $data->uid, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]); } catch (Exception $exception) { - Logger::notice('URL is invalid', ['url' => $data->url, 'error' => $exception]); + $this->logger->notice('URL is invalid', ['url' => $data->url, 'error' => $exception]); throw new ReferenceStorageException(sprintf('External resource failed to get %s', $reference), $exception->getCode(), $exception); } if (!empty($fetchResult) && $fetchResult->isSuccess()) { - Logger::debug('Got picture', ['Content-Type' => $fetchResult->getHeader('Content-Type'), 'uid' => $data->uid, 'url' => $data->url]); + $this->logger->debug('Got picture', ['Content-Type' => $fetchResult->getHeader('Content-Type'), 'uid' => $data->uid, 'url' => $data->url]); return $fetchResult->getBody(); } else { if (empty($fetchResult)) { From 5f6503a73fabbd65b9ddb1de430b73f472c53dfe Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Feb 2023 07:47:31 +0000 Subject: [PATCH 29/41] Editing/removing of attached pictures is now possible via web --- mod/item.php | 3 +- src/Content/Text/BBCode.php | 19 +++++++-- src/Model/Item.php | 30 ++++++++++---- src/Model/Post/Media.php | 52 +++++++++++++++++++----- src/Module/Post/Edit.php | 2 + src/Protocol/ActivityPub/Transmitter.php | 4 +- 6 files changed, 82 insertions(+), 28 deletions(-) diff --git a/mod/item.php b/mod/item.php index 13ffe75683..77cfc4795a 100644 --- a/mod/item.php +++ b/mod/item.php @@ -41,10 +41,8 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\ItemURI; -use Friendica\Model\Photo; use Friendica\Model\Post; use Friendica\Network\HTTPException; -use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; function item_post(App $a) { @@ -108,6 +106,7 @@ function item_edit(int $uid, array $request, bool $preview, string $return_path) $post['edit'] = $post; $post['file'] = Post\Category::getTextByURIId($post['uri-id'], $post['uid']); + Post\Media::deleteByURIId($post['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]); $post = item_process($post, $request, $preview, $return_path); $fields = [ diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 6730309e60..949f9d9cf0 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -437,7 +437,7 @@ class BBCode * @param boolean $no_link_desc No link description * @return string with replaced body */ - public static function removeAttachment(string $body, bool $no_link_desc = false): string + public static function replaceAttachment(string $body, bool $no_link_desc = false): string { return preg_replace_callback("/\s*\[attachment (.*?)\](.*?)\[\/attachment\]\s*/ism", function ($match) use ($body, $no_link_desc) { @@ -454,6 +454,17 @@ class BBCode }, $body); } + /** + * Remove [attachment] BBCode + * + * @param string $body + * @return string with removed attachment + */ + public static function removeAttachment(string $body): string + { + return trim(preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body)); + } + /** * Converts a BBCode text into plaintext * @@ -469,7 +480,7 @@ class BBCode $text = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $text); // Remove attachment - $text = self::removeAttachment($text); + $text = self::replaceAttachment($text); $naked_text = HTML::toPlaintext(self::convert($text, false, 0, true), 0, !$keep_urls); @@ -1583,9 +1594,9 @@ class BBCode /// @todo Have a closer look at the different html modes // Handle attached links or videos if (in_array($simple_html, [self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) { - $text = self::removeAttachment($text); + $text = self::replaceAttachment($text); } elseif (!in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::CONNECTORS])) { - $text = self::removeAttachment($text, true); + $text = self::replaceAttachment($text, true); } else { $text = self::convertAttachment($text, $simple_html, $try_oembed, [], $uriid); } diff --git a/src/Model/Item.php b/src/Model/Item.php index a62d3c2e6e..78275c8356 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -210,8 +210,6 @@ class Item } } - Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']); - $content_fields = ['raw-body' => trim($fields['raw-body'] ?? $fields['body'])]; // Remove all media attachments from the body and store them in the post-media table @@ -220,6 +218,10 @@ class Item $content_fields['raw-body'] = self::setHashtags($content_fields['raw-body']); Post\Media::insertFromRelevantUrl($item['uri-id'], $content_fields['raw-body'], $fields['body'], $item['author-network']); + + Post\Media::insertFromAttachmentData($item['uri-id'], $fields['body']); + $content_fields['raw-body'] = BBCode::removeAttachment($content_fields['raw-body']); + Post\Content::update($item['uri-id'], $content_fields); } @@ -1143,8 +1145,6 @@ class Item $item['body'] = BBCode::removeSharedData($item['body']); } - Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']); - // Remove all media attachments from the body and store them in the post-media table $item['raw-body'] = Post\Media::insertFromBody($item['uri-id'], $item['raw-body']); $item['raw-body'] = self::setHashtags($item['raw-body']); @@ -1152,6 +1152,10 @@ class Item $author = Contact::getById($item['author-id'], ['network']); Post\Media::insertFromRelevantUrl($item['uri-id'], $item['raw-body'], $item['body'], $author['network'] ?? ''); + Post\Media::insertFromAttachmentData($item['uri-id'], $item['body']); + $item['body'] = BBCode::removeAttachment($item['body']); + $item['raw-body'] = BBCode::removeAttachment($item['raw-body']); + // Check for hashtags in the body and repair or add hashtag links $item['body'] = self::setHashtags($item['body']); @@ -3003,8 +3007,9 @@ class Item $fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network', 'has-media', 'quote-uri-id', 'post-type']; - $shared_uri_id = 0; - $shared_links = []; + $shared_uri_id = 0; + $shared_links = []; + $quote_shared_links = []; $shared = DI::contentItem()->getSharedPost($item, $fields); if (!empty($shared['post'])) { @@ -3023,7 +3028,14 @@ class Item $shared_links[] = strtolower($media[0]['url']); } - $quote_uri_id = $shared_item['uri-id'] ?? 0; + if (!empty($shared_item['uri-id'])) { + $data = BBCode::getAttachmentData($shared_item['body']); + if (!empty($data['url'])) { + $quote_shared_links[] = $data['url']; + } + + $quote_uri_id = $shared_item['uri-id']; + } } } @@ -3098,7 +3110,7 @@ class Item if (!empty($shared_attachments)) { $s = self::addVisualAttachments($shared_attachments, $shared_item, $s, true); - $s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $shared_attachments, $body, $s, true, []); + $s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $shared_attachments, $body, $s, true, $quote_shared_links); $s = self::addNonVisualAttachments($shared_attachments, $item, $s, true); $body = BBCode::removeSharedData($body); } @@ -3418,7 +3430,7 @@ class Item DI::profiler()->stopRecording(); if (isset($data['url']) && !in_array(strtolower($data['url']), $ignore_links)) { - if (!empty($data['description']) || !empty($data['image']) || !empty($data['preview'])) { + if (!empty($data['description']) || !empty($data['image']) || !empty($data['preview']) || (!empty($data['title']) && !Strings::compareLink($data['title'], $data['url']))) { $parts = parse_url($data['url']); if (!empty($parts['scheme']) && !empty($parts['host'])) { if (empty($data['provider_name'])) { diff --git a/src/Model/Post/Media.php b/src/Model/Post/Media.php index 8216d73837..c5e355f3df 100644 --- a/src/Model/Post/Media.php +++ b/src/Model/Post/Media.php @@ -68,18 +68,18 @@ class Media * * @param array $media * @param bool $force - * @return void + * @return bool */ - public static function insert(array $media, bool $force = false) + public static function insert(array $media, bool $force = false): bool { if (empty($media['url']) || empty($media['uri-id']) || !isset($media['type'])) { Logger::warning('Incomplete media data', ['media' => $media]); - return; + return false; } if (DBA::exists('post-media', ['uri-id' => $media['uri-id'], 'preview' => $media['url']])) { Logger::info('Media already exists as preview', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'callstack' => System::callstack()]); - return; + return false; } // "document" has got the lowest priority. So when the same file is both attached as document @@ -87,12 +87,12 @@ class Media $found = DBA::selectFirst('post-media', ['type'], ['uri-id' => $media['uri-id'], 'url' => $media['url']]); if (!$force && !empty($found) && (($found['type'] != self::DOCUMENT) || ($media['type'] == self::DOCUMENT))) { Logger::info('Media already exists', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'callstack' => System::callstack()]); - return; + return false; } if (!ItemURI::exists($media['uri-id'])) { Logger::info('Media referenced URI ID not found', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'callstack' => System::callstack()]); - return; + return false; } $media = self::unsetEmptyFields($media); @@ -114,6 +114,7 @@ class Media } else { Logger::info('Nothing to update', ['media' => $media]); } + return $result; } /** @@ -573,9 +574,14 @@ class Media if (preg_match_all("/\[url\](https?:.*?)\[\/url\]/ism", $body, $matches)) { foreach ($matches[1] as $url) { Logger::info('Got page url (link without description)', ['uri-id' => $uriid, 'url' => $url]); - self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url], false, $network); - if ($network == Protocol::DFRN) { + $result = self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url], false, $network); + if ($result && ($network == Protocol::DFRN)) { self::revertHTMLType($uriid, $url, $fullbody); + Logger::debug('Revert HTML type', ['uri-id' => $uriid, 'url' => $url]); + } elseif ($result) { + Logger::debug('Media had been added', ['uri-id' => $uriid, 'url' => $url]); + } else { + Logger::debug('Media had not been added', ['uri-id' => $uriid, 'url' => $url]); } } } @@ -584,9 +590,14 @@ class Media if (preg_match_all("/\[url\=(https?:.*?)\].*?\[\/url\]/ism", $body, $matches)) { foreach ($matches[1] as $url) { Logger::info('Got page url (link with description)', ['uri-id' => $uriid, 'url' => $url]); - self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url], false, $network); - if ($network == Protocol::DFRN) { + $result = self::insert(['uri-id' => $uriid, 'type' => self::UNKNOWN, 'url' => $url], false, $network); + if ($result && ($network == Protocol::DFRN)) { self::revertHTMLType($uriid, $url, $fullbody); + Logger::debug('Revert HTML type', ['uri-id' => $uriid, 'url' => $url]); + } elseif ($result) { + Logger::debug('Media had been added', ['uri-id' => $uriid, 'url' => $url]); + } else { + Logger::debug('Media had not been added', ['uri-id' => $uriid, 'url' => $url]); } } } @@ -705,6 +716,25 @@ class Media return DBA::exists('post-media', $condition); } + /** + * Delete media by uri-id and media type + * + * @param int $uri_id URI id + * @param array $types Media types + * @return bool Whether media attachment exists + * @throws \Exception + */ + public static function deleteByURIId(int $uri_id, array $types = []): bool + { + $condition = ['uri-id' => $uri_id]; + + if (!empty($types)) { + $condition = DBA::mergeConditions($condition, ['type' => $types]); + } + + return DBA::delete('post-media', $condition); + } + /** * Split the attachment media in the three segments "visual", "link" and "additional" * @@ -830,7 +860,7 @@ class Media } $original_body = $body; - $body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body); + $body = BBCode::removeAttachment($body); foreach (self::getByURIId($uriid, $types) as $media) { if (Item::containsLink($body, $media['preview'] ?? $media['url'], $media['type'])) { diff --git a/src/Module/Post/Edit.php b/src/Module/Post/Edit.php index 55c86988ad..2ca9803818 100644 --- a/src/Module/Post/Edit.php +++ b/src/Module/Post/Edit.php @@ -115,6 +115,8 @@ class Edit extends BaseModule $lockstate = 'unlock'; } + $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); + $jotplugins = ''; Hook::callAll('jot_tool', $jotplugins); diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 02dc7ff544..9123ae533f 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1675,7 +1675,7 @@ class Transmitter if ($type == 'Page') { // When we transmit "Page" posts we have to remove the attachment. // The attachment contains the link that we already transmit in the "url" field. - $body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body); + $body = BBCode::removeAttachment($body); } $body = BBCode::setMentionsToNicknames($body); @@ -1707,7 +1707,7 @@ class Transmitter $richbody = DI::contentItem()->addSharedPost($item, $richbody); } } - $richbody = BBCode::removeAttachment($richbody); + $richbody = BBCode::replaceAttachment($richbody); $data['contentMap'][$language] = BBCode::convertForUriId($item['uri-id'], $richbody, BBCode::EXTERNAL); } From 0338d9fda83563d35880cbd8db313ed6545e1498 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Mon, 27 Feb 2023 14:31:56 +0100 Subject: [PATCH 30/41] Update src/Model/Post/Media.php Co-authored-by: Hypolite Petovan --- src/Model/Post/Media.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model/Post/Media.php b/src/Model/Post/Media.php index c5e355f3df..653bdba98a 100644 --- a/src/Model/Post/Media.php +++ b/src/Model/Post/Media.php @@ -595,9 +595,9 @@ class Media self::revertHTMLType($uriid, $url, $fullbody); Logger::debug('Revert HTML type', ['uri-id' => $uriid, 'url' => $url]); } elseif ($result) { - Logger::debug('Media had been added', ['uri-id' => $uriid, 'url' => $url]); + Logger::debug('Media has been added', ['uri-id' => $uriid, 'url' => $url]); } else { - Logger::debug('Media had not been added', ['uri-id' => $uriid, 'url' => $url]); + Logger::debug('Media has not been added', ['uri-id' => $uriid, 'url' => $url]); } } } From b375f64e0eaa2e842be29b6202d70746bcb67f84 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Mon, 27 Feb 2023 13:04:24 -0500 Subject: [PATCH 31/41] Add int hint to Mastodon V2 UserStats.php --- src/Object/Api/Mastodon/InstanceV2/UserStats.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Object/Api/Mastodon/InstanceV2/UserStats.php b/src/Object/Api/Mastodon/InstanceV2/UserStats.php index e6c8634ae6..d4349c9fe4 100644 --- a/src/Object/Api/Mastodon/InstanceV2/UserStats.php +++ b/src/Object/Api/Mastodon/InstanceV2/UserStats.php @@ -33,10 +33,10 @@ class UserStats extends BaseDataTransferObject /** @var int */ protected $active_month = 0; - /** - * @param $active_month - */ - public function __construct($active_month) + /** + * @param int $active_month + */ + public function __construct(int $active_month) { $this->active_month = $active_month; } From 2f3a7c422f1382f455d4a9a4d7ded0feda4a9261 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Mon, 27 Feb 2023 13:47:02 -0500 Subject: [PATCH 32/41] Fix whitespace issue --- src/Object/Api/Mastodon/InstanceV2/UserStats.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Object/Api/Mastodon/InstanceV2/UserStats.php b/src/Object/Api/Mastodon/InstanceV2/UserStats.php index d4349c9fe4..afe6048e5f 100644 --- a/src/Object/Api/Mastodon/InstanceV2/UserStats.php +++ b/src/Object/Api/Mastodon/InstanceV2/UserStats.php @@ -33,10 +33,10 @@ class UserStats extends BaseDataTransferObject /** @var int */ protected $active_month = 0; - /** - * @param int $active_month - */ - public function __construct(int $active_month) + /** + * @param int $active_month + */ + public function __construct(int $active_month) { $this->active_month = $active_month; } From d2b3cc515f7bc0e08a6f7b3efdafe0529572182a Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 19:51:04 +0100 Subject: [PATCH 33/41] Adapt .editorconfig for PHPStorm --- .editorconfig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index af67d04a3b..66406ca44e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,9 +6,13 @@ root = true [*] charset = utf-8 end_of_line = lf -trim_trailing_whitespaces = true indent_style = tab -quote_type = single +trim_trailing_whitespace = true +insert_final_newline = true -[*.js] -quote_type = double \ No newline at end of file +[*.yml] +indent_style = space +indent_size = 2 + +[composer.json] +indent_size = tab From 191c84a281623565956ab840e3f71029bec29994 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 20:01:16 +0100 Subject: [PATCH 34/41] adapt codecov config --- .codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codecov.yml b/.codecov.yml index ec27f44fea..7b6eecb66e 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,7 +1,7 @@ codecov: branch: develop ci: - - drone.friendi.ca + - ci.friendi.ca coverage: precision: 2 From 3c7184513c46d660140e887e565a1a275db602a4 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 20:11:07 +0100 Subject: [PATCH 35/41] Revert quote_type --- .editorconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.editorconfig b/.editorconfig index 66406ca44e..c4555b0821 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,11 @@ end_of_line = lf indent_style = tab trim_trailing_whitespace = true insert_final_newline = true +quote_type = single + +[*.js] +quote_type = double +ij_javascript_use_double_quotes = true [*.yml] indent_style = space From ae5fe47c03e51490eb027a00decc07b11c22aa8e Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 20:34:46 +0100 Subject: [PATCH 36/41] Adapt .editorconfig - Add .json rule - Add .xml rule --- .editorconfig | 11 ++++++++++- composer.json | 10 ++++++++-- docblox.dist.xml | 18 +++++++++--------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.editorconfig b/.editorconfig index c4555b0821..0cb6e3e97d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,7 @@ indent_style = tab trim_trailing_whitespace = true insert_final_newline = true quote_type = single +max_line_length = off [*.js] quote_type = double @@ -19,5 +20,13 @@ ij_javascript_use_double_quotes = true indent_style = space indent_size = 2 +[*.xml] +indent_style = space +indent_size = 4 + +[*.json] +indent_style = space +indent_size = 2 + [composer.json] -indent_size = tab +indent_style = tab diff --git a/composer.json b/composer.json index c6746c1231..e4e6aa6f03 100644 --- a/composer.json +++ b/composer.json @@ -132,7 +132,13 @@ "test": "phpunit", "lint": "find . -name \\*.php -not -path './vendor/*' -not -path './view/asset/*' -print0 | xargs -0 -n1 php -l", "cs:install": "@composer install --working-dir=bin/dev/php-cs-fixer", - "cs:check": ["@cs:install", "bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run --diff"], - "cs:fix": ["@cs:install", "bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer fix"] + "cs:check": [ + "@cs:install", + "bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run --diff" + ], + "cs:fix": [ + "@cs:install", + "bin/dev/php-cs-fixer/vendor/bin/php-cs-fixer fix" + ] } } diff --git a/docblox.dist.xml b/docblox.dist.xml index a2ea8e2cb7..aff0af91a7 100644 --- a/docblox.dist.xml +++ b/docblox.dist.xml @@ -1,13 +1,13 @@ - - data/output - - - data/output - - - . - + + data/output + + + data/output + + + . + From 089850e8f6f22c785bcd4a5e211b7c6576be2629 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 20:53:19 +0100 Subject: [PATCH 37/41] Add a PHPStorm code style XML --- mods/README.md | 10 +++++++--- mods/phpstorm-code-style.xml | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 mods/phpstorm-code-style.xml diff --git a/mods/README.md b/mods/README.md index fe7a7fed57..43389eb2c2 100644 --- a/mods/README.md +++ b/mods/README.md @@ -4,12 +4,12 @@ Friendica mods files ## `bookmarklet-share2friendica` Browser bookmarklet to share any page with your Friendica account. -Please see `bookmarklet-share2friendica/README.md` for detailed instruction. +Please see `bookmarklet-share2friendica/README.md` for detailed instruction. ## `fpostit` Node-agnostic Friendica bookmarklet by Devlon Duthie. -Unmaintained and unsupported. +Unmaintained and unsupported. ## `home.css` and `home.html` @@ -30,7 +30,11 @@ Please check software documentation to know how modify these examples to make th ## `sample-systemd.timer` and `sample-systemd.service` Sample systemd unit files to start worker.php periodically. - + Please place them in the correct location for your system, typically this is `/etc/systemd/system/friendicaworker.timer` and `/etc/systemd/system/friendicaworker.service`. Please report problems and improvements to `!helpers@forum.friendi.ca` and `@utzer@social.yl.ms` or open an issue in [the Github Friendica page](https://github.com/friendica/friendica/issues). This is for usage of systemd instead of cron to start the worker periodically, the solution is a work-in-progress and can surely be improved. + +## `phpstorm-code-style.xml` + +PHP Storm Code Style settings, used for this codebase diff --git a/mods/phpstorm-code-style.xml b/mods/phpstorm-code-style.xml new file mode 100644 index 0000000000..0fb86cfb01 --- /dev/null +++ b/mods/phpstorm-code-style.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file From ea550f6b02675f49964d7614e2eee2be4919656a Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 27 Feb 2023 20:57:19 +0100 Subject: [PATCH 38/41] ident_size 2 is better for XML readability :) --- .editorconfig | 2 +- docblox.dist.xml | 19 +++++++++---------- ruleset.xml | 24 ++++++++++++------------ 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0cb6e3e97d..610409143b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,7 +22,7 @@ indent_size = 2 [*.xml] indent_style = space -indent_size = 4 +indent_size = 2 [*.json] indent_style = space diff --git a/docblox.dist.xml b/docblox.dist.xml index aff0af91a7..d702bc8886 100644 --- a/docblox.dist.xml +++ b/docblox.dist.xml @@ -1,13 +1,12 @@ - - data/output - - - data/output - - - . - + + data/output + + + data/output + + + . + - diff --git a/ruleset.xml b/ruleset.xml index a71b7062fc..037b0e7b35 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,15 +1,15 @@ - Friendica Coding Standards: PSR2 with tabs instead of spaces - - - - - - - - - - - + Friendica Coding Standards: PSR2 with tabs instead of spaces + + + + + + + + + + + From 2849a96628274b694d17678f934c478baf571d09 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Feb 2023 20:02:05 +0000 Subject: [PATCH 39/41] Issue 12415: Handle attachment updates via AP --- src/Protocol/ActivityPub/Processor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 54acca9df3..4572bdaa60 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -242,6 +242,7 @@ class Processor $item['changed'] = DateTimeFormat::utcNow(); $item['edited'] = DateTimeFormat::utc($activity['updated']); + Post\Media::deleteByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]); $item = self::processContent($activity, $item); if (empty($item)) { Queue::remove($activity); From cbb9cb6c242f90919c623b50562ef7a788404f62 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Feb 2023 21:02:59 +0000 Subject: [PATCH 40/41] Fix v1 endpoint --- src/Object/Api/Mastodon/Instance.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Object/Api/Mastodon/Instance.php b/src/Object/Api/Mastodon/Instance.php index 5459694e6a..08df62c0f0 100644 --- a/src/Object/Api/Mastodon/Instance.php +++ b/src/Object/Api/Mastodon/Instance.php @@ -84,14 +84,14 @@ class Instance extends BaseDataTransferObject { $register_policy = intval($config->get('config', 'register_policy')); - $this->uri = $baseUrl; + $this->uri = $baseUrl->getHost(); $this->title = $config->get('config', 'sitename'); $this->short_description = $this->description = $config->get('config', 'info'); $this->email = implode(',', User::getAdminEmailList()); $this->version = '2.8.0 (compatible; Friendica ' . App::VERSION . ')'; $this->urls = null; // Not supported $this->stats = new Stats($config, $database); - $this->thumbnail = $baseUrl . 'images/friendica-banner.jpg'; + $this->thumbnail = $baseUrl->__toString() . '/images/friendica-banner.jpg'; $this->languages = [$config->get('system', 'language')]; $this->max_toot_chars = (int)$config->get('config', 'api_import_size', $config->get('config', 'max_import_size')); $this->registrations = ($register_policy != Register::CLOSED); From a5b774d2efa562742b6a16e0564b127ec29263db Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 27 Feb 2023 21:15:11 +0000 Subject: [PATCH 41/41] Without "->__toString()" --- src/Object/Api/Mastodon/Instance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Object/Api/Mastodon/Instance.php b/src/Object/Api/Mastodon/Instance.php index 08df62c0f0..118d491fc9 100644 --- a/src/Object/Api/Mastodon/Instance.php +++ b/src/Object/Api/Mastodon/Instance.php @@ -91,7 +91,7 @@ class Instance extends BaseDataTransferObject $this->version = '2.8.0 (compatible; Friendica ' . App::VERSION . ')'; $this->urls = null; // Not supported $this->stats = new Stats($config, $database); - $this->thumbnail = $baseUrl->__toString() . '/images/friendica-banner.jpg'; + $this->thumbnail = $baseUrl . '/images/friendica-banner.jpg'; $this->languages = [$config->get('system', 'language')]; $this->max_toot_chars = (int)$config->get('config', 'api_import_size', $config->get('config', 'max_import_size')); $this->registrations = ($register_policy != Register::CLOSED);
media-id String (Integer) ID used for attaching to a Mastodon Post StatusID used for attaching images to a Mastodon Post Status
scales Array of Photo Scales -List of Scale objects listing the id, scale, link, etc. of each scale +List of the various resized versions of the Photo