Merge branch 'develop' into new_image_presentation

This commit is contained in:
Marek Bachmann 2022-12-05 16:54:12 +01:00
parent fbd4b54656
commit 733543505b
33 changed files with 1438 additions and 705 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2022.12-dev (Giant Rhubarb) -- Friendica 2022.12-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1497 -- DB_UPDATE_VERSION 1500
-- ------------------------------------------ -- ------------------------------------------
@ -578,6 +578,40 @@ CREATE TABLE IF NOT EXISTS `delayed-post` (
FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE FOREIGN KEY (`wid`) REFERENCES `workerqueue` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Posts that are about to be distributed at a later time';
--
-- TABLE diaspora-contact
--
CREATE TABLE IF NOT EXISTS `diaspora-contact` (
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the contact URL',
`addr` varchar(255) COMMENT '',
`alias` varchar(255) COMMENT '',
`nick` varchar(255) COMMENT '',
`name` varchar(255) COMMENT '',
`given-name` varchar(255) COMMENT '',
`family-name` varchar(255) COMMENT '',
`photo` varchar(255) COMMENT '',
`photo-medium` varchar(255) COMMENT '',
`photo-small` varchar(255) COMMENT '',
`batch` varchar(255) COMMENT '',
`notify` varchar(255) COMMENT '',
`poll` varchar(255) COMMENT '',
`subscribe` varchar(255) COMMENT '',
`searchable` boolean COMMENT '',
`pubkey` text COMMENT '',
`gsid` int unsigned COMMENT 'Global Server ID',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`interacting_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts this contact interactes with',
`interacted_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts that interacted with this contact',
`post_count` int unsigned DEFAULT 0 COMMENT 'Number of posts and comments',
PRIMARY KEY(`uri-id`),
UNIQUE INDEX `addr` (`addr`),
INDEX `alias` (`alias`),
INDEX `gsid` (`gsid`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation';
-- --
-- TABLE diaspora-interaction -- TABLE diaspora-interaction
-- --
@ -633,39 +667,6 @@ CREATE TABLE IF NOT EXISTS `event` (
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
--
-- TABLE fcontact
--
CREATE TABLE IF NOT EXISTS `fcontact` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`guid` varbinary(255) NOT NULL DEFAULT '' COMMENT 'unique id',
`url` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the fcontact url',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`photo` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`request` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`addr` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`batch` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`notify` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`poll` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`confirm` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`network` char(4) NOT NULL DEFAULT '' COMMENT '',
`alias` varbinary(383) NOT NULL DEFAULT '' COMMENT '',
`pubkey` text COMMENT '',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`interacting_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts this contact interactes with',
`interacted_count` int unsigned DEFAULT 0 COMMENT 'Number of contacts that interacted with this contact',
`post_count` int unsigned DEFAULT 0 COMMENT 'Number of posts and comments',
PRIMARY KEY(`id`),
INDEX `addr` (`addr`(32)),
UNIQUE INDEX `url` (`url`(190)),
UNIQUE INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation';
-- --
-- TABLE fetch-entry -- TABLE fetch-entry
-- --
@ -2802,11 +2803,11 @@ CREATE VIEW `account-view` AS SELECT
`contact`.`blocked` AS `blocked`, `contact`.`blocked` AS `blocked`,
`contact`.`notify` AS `dfrn-notify`, `contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`, `contact`.`poll` AS `dfrn-poll`,
`fcontact`.`guid` AS `diaspora-guid`, `item-uri`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`, `diaspora-contact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`, `diaspora-contact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`, `diaspora-contact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`, `diaspora-contact`.`alias` AS `diaspora-alias`,
`apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`, `apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`, `apcontact`.`following` AS `ap-following`,
@ -2824,7 +2825,7 @@ CREATE VIEW `account-view` AS SELECT
FROM `contact` FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
WHERE `contact`.`uid` = 0; WHERE `contact`.`uid` = 0;
@ -2903,14 +2904,14 @@ CREATE VIEW `account-user-view` AS SELECT
`ucontact`.`reason` AS `reason`, `ucontact`.`reason` AS `reason`,
`contact`.`notify` AS `dfrn-notify`, `contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`, `contact`.`poll` AS `dfrn-poll`,
`fcontact`.`guid` AS `diaspora-guid`, `item-uri`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`, `diaspora-contact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`, `diaspora-contact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`, `diaspora-contact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`, `diaspora-contact`.`alias` AS `diaspora-alias`,
`fcontact`.`interacting_count` AS `diaspora-interacting_count`, `diaspora-contact`.`interacting_count` AS `diaspora-interacting_count`,
`fcontact`.`interacted_count` AS `diaspora-interacted_count`, `diaspora-contact`.`interacted_count` AS `diaspora-interacted_count`,
`fcontact`.`post_count` AS `diaspora-post_count`, `diaspora-contact`.`post_count` AS `diaspora-post_count`,
`apcontact`.`uuid` AS `ap-uuid`, `apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`, `apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`, `apcontact`.`following` AS `ap-following`,
@ -2929,7 +2930,7 @@ CREATE VIEW `account-user-view` AS SELECT
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`; LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`;
-- --
@ -3006,3 +3007,37 @@ CREATE VIEW `profile_field-view` AS SELECT
`profile_field`.`edited` AS `edited` `profile_field`.`edited` AS `edited`
FROM `profile_field` FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`; INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`;
--
-- VIEW diaspora-contact-view
--
DROP VIEW IF EXISTS `diaspora-contact-view`;
CREATE VIEW `diaspora-contact-view` AS SELECT
`diaspora-contact`.`uri-id` AS `uri-id`,
`item-uri`.`uri` AS `url`,
`item-uri`.`guid` AS `guid`,
`diaspora-contact`.`addr` AS `addr`,
`diaspora-contact`.`alias` AS `alias`,
`diaspora-contact`.`nick` AS `nick`,
`diaspora-contact`.`name` AS `name`,
`diaspora-contact`.`given-name` AS `given-name`,
`diaspora-contact`.`family-name` AS `family-name`,
`diaspora-contact`.`photo` AS `photo`,
`diaspora-contact`.`photo-medium` AS `photo-medium`,
`diaspora-contact`.`photo-small` AS `photo-small`,
`diaspora-contact`.`batch` AS `batch`,
`diaspora-contact`.`notify` AS `notify`,
`diaspora-contact`.`poll` AS `poll`,
`diaspora-contact`.`subscribe` AS `subscribe`,
`diaspora-contact`.`searchable` AS `searchable`,
`diaspora-contact`.`pubkey` AS `pubkey`,
`gserver`.`url` AS `baseurl`,
`diaspora-contact`.`gsid` AS `gsid`,
`diaspora-contact`.`created` AS `created`,
`diaspora-contact`.`updated` AS `updated`,
`diaspora-contact`.`interacting_count` AS `interacting_count`,
`diaspora-contact`.`interacted_count` AS `interacted_count`,
`diaspora-contact`.`post_count` AS `post_count`
FROM `diaspora-contact`
INNER JOIN `item-uri` ON `item-uri`.`id` = `diaspora-contact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = `diaspora-contact`.`gsid`;

View file

@ -179,26 +179,22 @@ The available features are client specific and may differ.
* [AndStatus](http://andstatus.org) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.andstatus.app), [Google Play](https://play.google.com/store/apps/details?id=org.andstatus.app)) * [AndStatus](http://andstatus.org) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.andstatus.app), [Google Play](https://play.google.com/store/apps/details?id=org.andstatus.app))
* [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma) * [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma)
* [Fedi](https://play.google.com/store/apps/details?id=com.fediverse.app) * [Fedi](https://github.com/Big-Fig/Fediverse.app) ([Google Play](https://play.google.com/store/apps/details?id=com.fediverse.app)
* [Fedilab](https://fedilab.app) ([F-Droid](https://f-droid.org/app/fr.gouv.etalab.mastodon), [Google Play](https://play.google.com/store/apps/details?id=app.fedilab.android)) * [Fedilab](https://fedilab.app) ([F-Droid](https://f-droid.org/app/fr.gouv.etalab.mastodon), [Google Play](https://play.google.com/store/apps/details?id=app.fedilab.android))
* [Friendiqa](https://git.friendi.ca/lubuwest/Friendiqa) ([F-Droid](https://git.friendi.ca/lubuwest/Friendiqa#install), [Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa)) * [Friendiqa](https://git.friendi.ca/lubuwest/Friendiqa) ([F-Droid](https://git.friendi.ca/lubuwest/Friendiqa#install), [Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa))
* [Husky](https://git.sr.ht/~captainepoch/husky) ([F-Droid](https://f-droid.org/repository/browse/?fdid=su.xash.husky), [Google Play](https://play.google.com/store/apps/details?id=su.xash.husky)) * [Husky](https://git.sr.ht/~captainepoch/husky) ([F-Droid](https://f-droid.org/repository/browse/?fdid=su.xash.husky), [Google Play](https://play.google.com/store/apps/details?id=su.xash.husky))
* [Mastodon for Android](https://github.com/mastodon/mastodon-android) (F-Droid: Pending, [Google-Play](https://play.google.com/store/apps/details?id=org.joinmastodon.android)) * [Mastodon](https://github.com/mastodon/mastodon-android) ([F-Droid](https://f-droid.org/en/packages/org.joinmastodon.android/), [Google Play](https://play.google.com/store/apps/details?id=org.joinmastodon.android))
* [Subway Tooter](https://github.com/tateisu/SubwayTooter) * [Subway Tooter](https://github.com/tateisu/SubwayTooter) ([F-Droid](https://android.izzysoft.de/repo/apk/jp.juggler.subwaytooter))
* [Tooot](https://tooot.app/) * [Tooot](https://tooot.app/) ([Google Play](https://play.google.com/store/apps/details?id=com.xmflsct.app.tooot))
* [Tusky](https://tusky.app) ([F-Droid](https://f-droid.org/repository/browse/?fdid=com.keylesspalace.tusky), [Google Play](https://play.google.com/store/apps/details?id=com.keylesspalace.tusky)) * [Tusky](https://tusky.app) ([F-Droid](https://f-droid.org/repository/browse/?fdid=com.keylesspalace.tusky), [Google Play](https://play.google.com/store/apps/details?id=com.keylesspalace.tusky))
* [Twidere](https://github.com/TwidereProject/Twidere-Android) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.mariotaku.twidere), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex)) * [Twidere](https://github.com/TwidereProject/Twidere-Android) ([F-Droid](https://f-droid.org/repository/browse/?fdid=org.mariotaku.twidere), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex))
* [TwidereX](https://github.com/TwidereProject/TwidereX-Android) ([F-Droid](https://f-droid.org/en/packages/com.twidere.twiderex/), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex)) * [TwidereX](https://github.com/TwidereProject/TwidereX-Android) ([F-Droid](https://f-droid.org/en/packages/com.twidere.twiderex/), [Google Play](https://play.google.com/store/apps/details?id=com.twidere.twiderex))
* [Yuito](https://github.com/accelforce/Yuito) ([Google Play](https://play.google.com/store/apps/details?id=net.accelf.yuito)) * [Yuito](https://github.com/accelforce/Yuito) ([Google Play](https://play.google.com/store/apps/details?id=net.accelf.yuito))
#### SailfishOS
* [Friendly](https://openrepos.net/content/fabrixxm/friendly), last update: 2018
#### iOS #### iOS
* [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma) ([AppStore](https://apps.apple.com/app/b4x-pleroma/id1538396871)) * [B4X for Pleroma & Mastodon](https://github.com/AnywhereSoftware/B4X-Pleroma) ([AppStore](https://apps.apple.com/app/b4x-pleroma/id1538396871))
* [Fedi](https://fediapp.com) ([AppStore](https://apps.apple.com/de/app/fedi-for-pleroma-and-mastodon/id1478806281)) * [Fedi](https://github.com/Big-Fig/Fediverse.app) ([AppStore](https://apps.apple.com/de/app/fedi-for-pleroma-and-mastodon/id1478806281))
* [Mastodon for iPhone and iPad](https://joinmastodon.org/apps) ([AppStore](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974)) * [Mastodon for iPhone and iPad](https://joinmastodon.org/apps) ([AppStore](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974))
* [Stella*](https://www.stella-app.net/) ([AppStore](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048)) * [Stella*](https://www.stella-app.net/) ([AppStore](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048))
* [Tooot](https://github.com/tooot-app) ([AppStore](https://apps.apple.com/app/id1549772269), Data collection (not linked to identity) * [Tooot](https://github.com/tooot-app) ([AppStore](https://apps.apple.com/app/id1549772269), Data collection (not linked to identity)
@ -207,18 +203,19 @@ The available features are client specific and may differ.
#### Linux #### Linux
* [Choqok](https://choqok.kde.org) * [Choqok](https://choqok.kde.org)
* [Whalebird](https://whalebird.social) * [Whalebird](https://whalebird.social/en/desktop/contents) ([GitHub](https://github.com/h3poteto/whalebird-desktop))
* [TheDesk](https://ja.mstdn.wiki/TheDesk) * [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Toot](https://toot.readthedocs.io/en/latest/) * [Toot](https://toot.readthedocs.io/en/latest/)
* [Tootle](https://github.com/bleakgrey/tootle) * [Tootle](https://github.com/bleakgrey/tootle)
#### macOS #### macOS
* [Mastonaut](https://mastonaut.app/) ([AppStore](https://apps.apple.com/us/app/mastonaut/id1450757574)), closed source * [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([AppStore](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop)) * [Whalebird](https://whalebird.social/en/desktop/contents) ([AppStore](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop))
#### Windows #### Windows
* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
* [Whalebird](https://whalebird.social/en/desktop/contents) ([Website Download](https://whalebird.social/en/desktop/contents/downloads#windows), [GitHub](https://github.com/h3poteto/whalebird-desktop)) * [Whalebird](https://whalebird.social/en/desktop/contents) ([Website Download](https://whalebird.social/en/desktop/contents/downloads#windows), [GitHub](https://github.com/h3poteto/whalebird-desktop))
#### Web Frontend #### Web Frontend

View file

@ -23,10 +23,10 @@ Database Tables
| [contact-relation](help/database/db_contact-relation) | Contact relations | | [contact-relation](help/database/db_contact-relation) | Contact relations |
| [conv](help/database/db_conv) | private messages | | [conv](help/database/db_conv) | private messages |
| [delayed-post](help/database/db_delayed-post) | Posts that are about to be distributed at a later time | | [delayed-post](help/database/db_delayed-post) | Posts that are about to be distributed at a later time |
| [diaspora-contact](help/database/db_diaspora-contact) | Diaspora compatible contacts - used in the Diaspora implementation |
| [diaspora-interaction](help/database/db_diaspora-interaction) | Signed Diaspora Interaction | | [diaspora-interaction](help/database/db_diaspora-interaction) | Signed Diaspora Interaction |
| [endpoint](help/database/db_endpoint) | ActivityPub endpoints - used in the ActivityPub implementation | | [endpoint](help/database/db_endpoint) | ActivityPub endpoints - used in the ActivityPub implementation |
| [event](help/database/db_event) | Events | | [event](help/database/db_event) | Events |
| [fcontact](help/database/db_fcontact) | Diaspora compatible contacts - used in the Diaspora implementation |
| [fetch-entry](help/database/db_fetch-entry) | | | [fetch-entry](help/database/db_fetch-entry) | |
| [fetched-activity](help/database/db_fetched-activity) | Id of fetched activities | | [fetched-activity](help/database/db_fetched-activity) | Id of fetched activities |
| [fsuggest](help/database/db_fsuggest) | friend suggestion stuff | | [fsuggest](help/database/db_fsuggest) | friend suggestion stuff |

View file

@ -0,0 +1,52 @@
Table diaspora-contact
===========
Diaspora compatible contacts - used in the Diaspora implementation
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ----------------- | ------------------------------------------------------------ | ------------ | ---- | --- | ------------------- | ----- |
| uri-id | Id of the item-uri table entry that contains the contact URL | int unsigned | NO | PRI | NULL | |
| addr | | varchar(255) | YES | | NULL | |
| alias | | varchar(255) | YES | | NULL | |
| nick | | varchar(255) | YES | | NULL | |
| name | | varchar(255) | YES | | NULL | |
| given-name | | varchar(255) | YES | | NULL | |
| family-name | | varchar(255) | YES | | NULL | |
| photo | | varchar(255) | YES | | NULL | |
| photo-medium | | varchar(255) | YES | | NULL | |
| photo-small | | varchar(255) | YES | | NULL | |
| batch | | varchar(255) | YES | | NULL | |
| notify | | varchar(255) | YES | | NULL | |
| poll | | varchar(255) | YES | | NULL | |
| subscribe | | varchar(255) | YES | | NULL | |
| searchable | | boolean | YES | | NULL | |
| pubkey | | text | YES | | NULL | |
| gsid | Global Server ID | int unsigned | YES | | NULL | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
| interacting_count | Number of contacts this contact interactes with | int unsigned | YES | | 0 | |
| interacted_count | Number of contacts that interacted with this contact | int unsigned | YES | | 0 | |
| post_count | Number of posts and comments | int unsigned | YES | | 0 | |
Indexes
------------
| Name | Fields |
| ------- | ------------ |
| PRIMARY | uri-id |
| addr | UNIQUE, addr |
| alias | alias |
| gsid | gsid |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
| gsid | [gserver](help/database/db_gserver) | id |
Return to [database documentation](help/database)

View file

@ -1,51 +0,0 @@
Table fcontact
===========
Diaspora compatible contacts - used in the Diaspora implementation
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ----------------- | ------------------------------------------------------------- | ---------------- | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | unique id | varbinary(255) | NO | | | |
| url | | varbinary(383) | NO | | | |
| uri-id | Id of the item-uri table entry that contains the fcontact url | int unsigned | YES | | NULL | |
| name | | varchar(255) | NO | | | |
| photo | | varbinary(383) | NO | | | |
| request | | varbinary(383) | NO | | | |
| nick | | varchar(255) | NO | | | |
| addr | | varchar(255) | NO | | | |
| batch | | varbinary(383) | NO | | | |
| notify | | varbinary(383) | NO | | | |
| poll | | varbinary(383) | NO | | | |
| confirm | | varbinary(383) | NO | | | |
| priority | | tinyint unsigned | NO | | 0 | |
| network | | char(4) | NO | | | |
| alias | | varbinary(383) | NO | | | |
| pubkey | | text | YES | | NULL | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
| interacting_count | Number of contacts this contact interactes with | int unsigned | YES | | 0 | |
| interacted_count | Number of contacts that interacted with this contact | int unsigned | YES | | 0 | |
| post_count | Number of posts and comments | int unsigned | YES | | 0 | |
Indexes
------------
| Name | Fields |
| ------- | ---------------- |
| PRIMARY | id |
| addr | addr(32) |
| url | UNIQUE, url(190) |
| uri-id | UNIQUE, uri-id |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database)

View file

@ -131,9 +131,9 @@ HELP;
$this->out('Updating event table fields'); $this->out('Updating event table fields');
$this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url); $this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url);
$this->out('Updating fcontact table fields'); $this->out('Updating diaspora-contact table fields');
$this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url); $this->database->replaceInTableFields('diaspora-contact', ['alias', 'photo', 'photo-medium', 'photo-small', 'batch', 'notify', 'poll', 'subscribe'], $old_url, $new_url);
$this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host); $this->database->replaceInTableFields('diaspora-contact', ['addr'], $old_host, $new_host);
$this->out('Updating fsuggest table fields'); $this->out('Updating fsuggest table fields');
$this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url); $this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url);

View file

@ -599,6 +599,11 @@ abstract class DI
return self::$dice->create(Protocol\Activity::class); return self::$dice->create(Protocol\Activity::class);
} }
public static function dsprContact(): Protocol\Diaspora\Repository\DiasporaContact
{
return self::$dice->create(Protocol\Diaspora\Repository\DiasporaContact::class);
}
// //
// "Security" namespace instances // "Security" namespace instances
// //

View file

@ -74,7 +74,7 @@ class DBStructure
$old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data', $old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data',
'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule', 'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule',
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge', 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge',
'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation']; 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact'];
$tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'], $tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']);

View file

@ -833,7 +833,7 @@ class Database
/** /**
* Check if data exists * Check if data exists
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* *
* @return boolean Are there rows for that condition? * @return boolean Are there rows for that condition?
@ -1017,7 +1017,7 @@ class Database
/** /**
* Insert a row into a table. Field value objects will be cast as string. * Insert a row into a table. Field value objects will be cast as string.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $param parameter array * @param array $param parameter array
* @param int $duplicate_mode What to do on a duplicated entry * @param int $duplicate_mode What to do on a duplicated entry
* *
@ -1068,7 +1068,7 @@ class Database
* Inserts a row with the provided data in the provided table. * Inserts a row with the provided data in the provided table.
* If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead. * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $param parameter array * @param array $param parameter array
* @return boolean was the insert successful? * @return boolean was the insert successful?
* @throws \Exception * @throws \Exception
@ -1116,7 +1116,7 @@ class Database
* *
* This function can be extended in the future to accept a table array as well. * This function can be extended in the future to accept a table array as well.
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @return boolean was the lock successful? * @return boolean was the lock successful?
* @throws \Exception * @throws \Exception
*/ */
@ -1314,7 +1314,7 @@ class Database
* Only set $old_fields to a boolean value when you are sure that you will update a single row. * Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields! * When you set $old_fields to "true" then $fields must contain all relevant fields!
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields contains the fields that are updated * @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values * @param array $condition condition array with the key values
* @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields) * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
@ -1380,7 +1380,7 @@ class Database
/** /**
* Retrieve a single record from a table and returns it in an associative array * Retrieve a single record from a table and returns it in an associative array
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1406,7 +1406,7 @@ class Database
/** /**
* Select rows from a table and fills an array with the data * Select rows from a table and fills an array with the data
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1479,7 +1479,7 @@ class Database
* *
* $data = DBA::select($table, $fields, $condition, $params); * $data = DBA::select($table, $fields, $condition, $params);
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
@ -1519,7 +1519,7 @@ class Database
/** /**
* Counts the rows from a table satisfying the provided condition * Counts the rows from a table satisfying the provided condition
* *
* @param string $table Table name in format schema.table (while scheme is optiona) * @param string $table Table name in format [schema.]table
* @param array $condition Array of fields for condition * @param array $condition Array of fields for condition
* @param array $params Array of several parameters * @param array $params Array of several parameters
* *

View file

@ -899,6 +899,11 @@ class PostUpdate
return true; return true;
} }
if (!DBStructure::existsTable('fcontact')) {
DI::config()->set('system', 'post_update_version', 1425);
return true;
}
$condition = ["`uri-id` IS NULL"]; $condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('fcontact', $condition)]); Logger::info('Start', ['rest' => DBA::count('fcontact', $condition)]);

View file

@ -86,7 +86,7 @@ class Status extends BaseFactory
*/ */
public function createFromUriId(int $uriId, int $uid = 0, bool $reblog = true): \Friendica\Object\Api\Mastodon\Status public function createFromUriId(int $uriId, int $uid = 0, bool $reblog = true): \Friendica\Object\Api\Mastodon\Status
{ {
$fields = ['uri-id', 'uid', 'author-id', 'author-uri-id', 'author-link', 'causer-uri-id', 'post-reason', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id', $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'];
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]); $item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
if (!$item) { if (!$item) {
@ -97,9 +97,18 @@ class Status extends BaseFactory
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.')); throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
} }
$is_reshare = $reblog && !is_null($item['causer-uri-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT); if (($item['gravity'] == Item::GRAVITY_ACTIVITY) && ($item['vid'] == Verb::getID(Activity::ANNOUNCE))) {
$is_reshare = true;
$account = $this->mstdnAccountFactory->createFromUriId($is_reshare ? $item['causer-uri-id']:$item['author-uri-id'], $uid); $account = $this->mstdnAccountFactory->createFromUriId($item['author-uri-id'], $uid);
$uriId = $item['thr-parent-id'];
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
if (!$item) {
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
}
} else {
$is_reshare = $reblog && !is_null($item['causer-uri-id']) && ($item['causer-id'] != $item['author-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT);
$account = $this->mstdnAccountFactory->createFromUriId($is_reshare ? $item['causer-uri-id'] : $item['author-uri-id'], $uid);
}
$count_announce = Post::countPosts([ $count_announce = Post::countPosts([
'thr-parent-id' => $uriId, 'thr-parent-id' => $uriId,
@ -190,19 +199,13 @@ class Status extends BaseFactory
} }
} }
if ($item['vid'] == Verb::getID(Activity::ANNOUNCE)) { $item['body'] = $this->contentItem->addSharedPost($item);
$reshare = $this->createFromUriId($item['thr-parent-id'], $uid)->toArray(); $item['raw-body'] = $this->contentItem->addSharedPost($item, $item['raw-body']);
$reshared_item = Post::selectFirst(['title', 'body'], ['uri-id' => $item['thr-parent-id'],'uid' => [0, $uid]]);
$item['title'] = $reshared_item['title'] ?? $item['title'];
$item['body'] = $reshared_item['body'] ?? $item['body'];
} else {
$item['body'] = $this->contentItem->addSharedPost($item);
$item['raw-body'] = $this->contentItem->addSharedPost($item, $item['raw-body']);
$reshare = [];
}
if ($is_reshare) { if ($is_reshare) {
$reshare = $this->createFromUriId($uriId, $uid, false)->toArray(); $reshare = $this->createFromUriId($uriId, $uid, false)->toArray();
} else {
$reshare = [];
} }
return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $reshare, $poll); return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $reshare, $poll);

View file

@ -1395,7 +1395,7 @@ class Contact
} }
if ($data['network'] == Protocol::DIASPORA) { if ($data['network'] == Protocol::DIASPORA) {
FContact::updateFromProbeArray($data); DI::dsprContact()->updateFromProbeArray($data);
} }
self::updateFromProbeArray($contact_id, $data); self::updateFromProbeArray($contact_id, $data);
@ -2057,9 +2057,10 @@ class Contact
* @param integer $cid contact id * @param integer $cid contact id
* @param string $size One of the Proxy::SIZE_* constants * @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date * @param string $updated Contact update date
* @param bool $static If "true" a parameter is added to convert the avatar to a static one
* @return string avatar link * @return string avatar link
*/ */
public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = '', string $guid = ''): string public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string
{ {
// We have to fetch the "updated" variable when it wasn't provided // We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance // The parameter can be provided to improve performance
@ -2089,7 +2090,15 @@ class Contact
$url .= Proxy::PIXEL_LARGE . '/'; $url .= Proxy::PIXEL_LARGE . '/';
break; break;
} }
return $url . ($guid ?: $cid) . ($updated ? '?ts=' . strtotime($updated) : ''); $query_params = [];
if ($updated) {
$query_params['ts'] = strtotime($updated);
}
if ($static) {
$query_params['static'] = true;
}
return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
} }
/** /**
@ -2114,9 +2123,10 @@ class Contact
* @param integer $cid contact id * @param integer $cid contact id
* @param string $size One of the Proxy::SIZE_* constants * @param string $size One of the Proxy::SIZE_* constants
* @param string $updated Contact update date * @param string $updated Contact update date
* @param bool $static If "true" a parameter is added to convert the header to a static one
* @return string header link * @return string header link
*/ */
public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = '', string $guid = ''): string public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = '', string $guid = '', bool $static = false): string
{ {
// We have to fetch the "updated" variable when it wasn't provided // We have to fetch the "updated" variable when it wasn't provided
// The parameter can be provided to improve performance // The parameter can be provided to improve performance
@ -2147,7 +2157,15 @@ class Contact
break; break;
} }
return $url . ($guid ?: $cid) . ($updated ? '?ts=' . strtotime($updated) : ''); $query_params = [];
if ($updated) {
$query_params['ts'] = strtotime($updated);
}
if ($static) {
$query_params['static'] = true;
}
return $url . ($guid ?: $cid) . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
} }
/** /**
@ -2468,7 +2486,7 @@ class Contact
$ret = Probe::uri($contact['url'], $network, $contact['uid']); $ret = Probe::uri($contact['url'], $network, $contact['uid']);
if ($ret['network'] == Protocol::DIASPORA) { if ($ret['network'] == Protocol::DIASPORA) {
FContact::updateFromProbeArray($ret); DI::dsprContact()->updateFromProbeArray($ret);
} }
return self::updateFromProbeArray($id, $ret); return self::updateFromProbeArray($id, $ret);

View file

@ -1,161 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Model;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Item;
use Friendica\Network\Probe;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
class FContact
{
/**
* Fetches data for a given handle
*
* @param string $handle The handle
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
*
* @return array the queried data
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function getByURL(string $handle, $update = null): array
{
Logger::debug('Fetch fcontact', ['handle' => $handle, 'update' => $update]);
$person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'addr' => $handle]);
if (!DBA::isResult($person)) {
$urls = [$handle, str_replace('http://', 'https://', $handle), Strings::normaliseLink($handle)];
$person = DBA::selectFirst('fcontact', [], ['network' => Protocol::DIASPORA, 'url' => $urls]);
}
if (DBA::isResult($person)) {
Logger::debug('In cache', ['handle' => $handle]);
if (is_null($update)) {
$update = empty($person['guid']) || empty($person['uri-id']) || ($person['created'] <= DBA::NULL_DATETIME);
if (GServer::getNextUpdateDate(true, $person['created'], $person['updated'], false) < DateTimeFormat::utcNow()) {
Logger::debug('Start background update', ['handle' => $handle]);
Worker::add(['priority' => Worker::PRIORITY_LOW, 'dont_fork' => true], 'UpdateFContact', $handle);
}
}
} elseif (is_null($update)) {
$update = true;
} else {
$person = [];
}
if ($update) {
Logger::info('create or refresh', ['handle' => $handle]);
$data = Probe::uri($handle, Protocol::DIASPORA);
// Note that Friendica contacts will return a "Diaspora person"
// if Diaspora connectivity is enabled on their server
if ($data['network'] ?? '' === Protocol::DIASPORA) {
self::updateFromProbeArray($data);
$person = self::getByURL($handle, false);
}
}
return $person;
}
/**
* Updates the fcontact table
*
* @param array $arr The fcontact data
* @throws \Exception
*/
public static function updateFromProbeArray(array $arr)
{
$uriid = ItemURI::insert(['uri' => $arr['url'], 'guid' => $arr['guid']]);
$fcontact = DBA::selectFirst('fcontact', ['created'], ['url' => $arr['url'], 'network' => $arr['network']]);
$contact = Contact::getByUriId($uriid, ['id', 'created']);
$apcontact = APContact::getByURL($arr['url'], false);
if (!empty($apcontact)) {
$interacted = $apcontact['following_count'];
$interacting = $apcontact['followers_count'];
$posts = $apcontact['statuses_count'];
} elseif (!empty($contact['id'])) {
$last_interaction = DateTimeFormat::utc('now - 180 days');
$interacted = DBA::count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$interacting = DBA::count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$posts = DBA::count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
}
$fields = [
'name' => $arr['name'],
'photo' => $arr['photo'],
'request' => $arr['request'],
'nick' => $arr['nick'],
'addr' => strtolower($arr['addr']),
'guid' => $arr['guid'],
'batch' => $arr['batch'],
'notify' => $arr['notify'],
'poll' => $arr['poll'],
'confirm' => $arr['confirm'],
'alias' => $arr['alias'],
'pubkey' => $arr['pubkey'],
'uri-id' => $uriid,
'interacting_count' => $interacting ?? 0,
'interacted_count' => $interacted ?? 0,
'post_count' => $posts ?? 0,
'updated' => DateTimeFormat::utcNow(),
];
if (empty($fcontact['created'])) {
$fields['created'] = $fields['updated'];
} elseif (!empty($contact['created']) && ($fcontact['created'] <= DBA::NULL_DATETIME)) {
$fields['created'] = $contact['created'];
}
$fields = DI::dbaDefinition()->truncateFieldsForTable('fcontact', $fields);
DBA::update('fcontact', $fields, ['url' => $arr['url'], 'network' => $arr['network']], true);
}
/**
* get a url (scheme://domain.tld/u/user) from a given Diaspora*
* fcontact guid
*
* @param string $fcontact_guid Hexadecimal string guid
* @return string|null the contact url or null
* @throws \Exception
*/
public static function getUrlByGuid(string $fcontact_guid)
{
Logger::info('fcontact', ['guid' => $fcontact_guid]);
$fcontact = DBA::selectFirst('fcontact', ['url'], ["`url` != ? AND `network` = ? AND `guid` = ?", '', Protocol::DIASPORA, $fcontact_guid]);
if (DBA::isResult($fcontact)) {
return $fcontact['url'];
}
return null;
}
}

View file

@ -80,8 +80,15 @@ class Statuses extends BaseApi
} }
if (!$request['pinned'] && !$request['only_media']) { if (!$request['pinned'] && !$request['only_media']) {
$condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))", if ($request['exclude_replies']) {
Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]); $condition = DBA::mergeConditions($condition, ["(`gravity` = ? OR (`gravity` = ? AND `vid` = ?))",
Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
} else {
$condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ?))",
Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE)]);
}
} elseif ($request['exclude_replies']) {
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
} }
if (!empty($request['max_id'])) { if (!empty($request['max_id'])) {
@ -97,16 +104,10 @@ class Statuses extends BaseApi
$params['order'] = ['uri-id']; $params['order'] = ['uri-id'];
} }
if (($request['pinned'] || $request['only_media']) && $request['exclude_replies']) {
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
}
if ($request['pinned']) { if ($request['pinned']) {
$items = DBA::select('collection-view', ['uri-id'], $condition, $params); $items = DBA::select('collection-view', ['uri-id'], $condition, $params);
} elseif ($request['only_media']) { } elseif ($request['only_media']) {
$items = DBA::select('media-view', ['uri-id'], $condition, $params); $items = DBA::select('media-view', ['uri-id'], $condition, $params);
} elseif ($request['exclude_replies']) {
$items = Post::selectThreadForUser($uid, ['uri-id'], $condition, $params);
} else { } else {
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params); $items = Post::selectForUser($uid, ['uri-id'], $condition, $params);
} }

View file

@ -23,7 +23,6 @@ namespace Friendica\Module;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\APContact; use Friendica\Model\APContact;
use Friendica\Model\User; use Friendica\Model\User;

View file

@ -182,6 +182,12 @@ class Photo extends BaseModule
throw new HTTPException\InternalServerErrorException($error); throw new HTTPException\InternalServerErrorException($error);
} }
if (!empty($request['static'])) {
$img = new Image($imgdata, $photo['type']);
$img->toStatic();
$imgdata = $img->asString();
}
// if customsize is set and image is not a gif, resize it // if customsize is set and image is not a gif, resize it
if ($photo['type'] !== 'image/gif' && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) { if ($photo['type'] !== 'image/gif' && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) {
$img = new Image($imgdata, $photo['type']); $img = new Image($imgdata, $photo['type']);

View file

@ -173,7 +173,7 @@ class Status extends BaseProfile
$condition = DBA::mergeConditions($condition, ["((`gravity` = ? AND `wall`) OR $condition = DBA::mergeConditions($condition, ["((`gravity` = ? AND `wall`) OR
(`gravity` = ? AND `vid` = ? AND `origin` (`gravity` = ? AND `vid` = ? AND `origin`
AND `thr-parent-id` IN (SELECT `uri-id` FROM `post` WHERE `gravity` = ? AND `network` IN (?, ?))))", AND EXISTS(SELECT `uri-id` FROM `post` WHERE `uri-id` = `post-user-view`.`thr-parent-id` AND `gravity` = ? AND `network` IN (?, ?))))",
Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Item::GRAVITY_PARENT, Protocol::ACTIVITYPUB, Protocol::DFRN]); Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Item::GRAVITY_PARENT, Protocol::ACTIVITYPUB, Protocol::DFRN]);
$condition = DBA::mergeConditions($condition, ['uid' => $profile['uid'], 'network' => Protocol::FEDERATED, $condition = DBA::mergeConditions($condition, ['uid' => $profile['uid'], 'network' => Protocol::FEDERATED,

View file

@ -110,7 +110,8 @@ class Probe
*/ */
private static function rearrangeData(array $data): array private static function rearrangeData(array $data): array
{ {
$fields = ['name', 'nick', 'guid', 'url', 'addr', 'alias', 'photo', 'header', $fields = ['name', 'given_name', 'family_name', 'nick', 'guid', 'url', 'addr', 'alias',
'photo', 'photo_medium', 'photo_small', 'header',
'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix', 'account-type', 'community', 'keywords', 'location', 'about', 'xmpp', 'matrix',
'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco', 'hide', 'batch', 'notify', 'poll', 'request', 'confirm', 'subscribe', 'poco',
'following', 'followers', 'inbox', 'outbox', 'sharedinbox', 'following', 'followers', 'inbox', 'outbox', 'sharedinbox',
@ -124,7 +125,7 @@ class Probe
if (in_array($field, $numeric_fields)) { if (in_array($field, $numeric_fields)) {
$newdata[$field] = (int)$data[$field]; $newdata[$field] = (int)$data[$field];
} else { } else {
$newdata[$field] = $data[$field]; $newdata[$field] = trim($data[$field]);
} }
} elseif (!in_array($field, $numeric_fields)) { } elseif (!in_array($field, $numeric_fields)) {
$newdata[$field] = ''; $newdata[$field] = '';
@ -1290,9 +1291,19 @@ class Probe
$data['name'] = $search->item(0)->nodeValue; $data['name'] = $search->item(0)->nodeValue;
} }
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' given_name ')]", $vcard); // */
if ($search->length > 0) {
$data["given_name"] = $search->item(0)->nodeValue;
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' family_name ')]", $vcard); // */
if ($search->length > 0) {
$data["family_name"] = $search->item(0)->nodeValue;
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */
if ($search->length > 0) { if ($search->length > 0) {
$data['searchable'] = $search->item(0)->nodeValue; $data['hide'] = (strtolower($search->item(0)->nodeValue) != 'true');
} }
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */ $search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
@ -1309,7 +1320,7 @@ class Probe
} }
} }
$avatar = []; $avatars = [];
if (!empty($vcard)) { if (!empty($vcard)) {
$photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */ $photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */
foreach ($photos as $photo) { foreach ($photos as $photo) {
@ -1319,20 +1330,27 @@ class Probe
} }
if (isset($attr['src']) && isset($attr['width'])) { if (isset($attr['src']) && isset($attr['width'])) {
$avatar[$attr['width']] = $attr['src']; $avatars[$attr['width']] = self::fixAvatar($attr['src'], $data['baseurl']);
} }
// We don't have a width. So we just take everything that we got. // We don't have a width. So we just take everything that we got.
// This is a Hubzilla workaround which doesn't send a width. // This is a Hubzilla workaround which doesn't send a width.
if ((sizeof($avatar) == 0) && !empty($attr['src'])) { if (!$avatars && !empty($attr['src'])) {
$avatar[] = $attr['src']; $avatars[] = self::fixAvatar($attr['src'], $data['baseurl']);
} }
} }
} }
if (sizeof($avatar)) { if ($avatars) {
ksort($avatar); ksort($avatars);
$data['photo'] = self::fixAvatar(array_pop($avatar), $data['baseurl']); $data['photo'] = array_pop($avatars);
if ($avatars) {
$data['photo_medium'] = array_pop($avatars);
}
if ($avatars) {
$data['photo_small'] = array_pop($avatars);
}
} }
if ($dfrn) { if ($dfrn) {
@ -1356,7 +1374,6 @@ class Probe
} }
} }
return $data; return $data;
} }

View file

@ -109,9 +109,9 @@ class Account extends BaseDataTransferObject
$this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL); $this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL);
$this->url = $account['url']; $this->url = $account['url'];
$this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? ''); $this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '');
$this->avatar_static = $this->avatar; $this->avatar_static = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '', true);
$this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? ''); $this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '');
$this->header_static = $this->header; $this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true);
$this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0; $this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0;
$this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0; $this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0;
$this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0; $this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0;

View file

@ -25,6 +25,7 @@ use Exception;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Images; use Friendica\Util\Images;
use Imagick; use Imagick;
use ImagickDraw;
use ImagickPixel; use ImagickPixel;
use GDImage; use GDImage;
use kornrunner\Blurhash\Blurhash; use kornrunner\Blurhash\Blurhash;
@ -64,7 +65,7 @@ class Image
} }
$this->type = $type; $this->type = $type;
if ($this->isImagick() && $this->loadData($data)) { if ($this->isImagick() && (empty($data) || $this->loadData($data))) {
return; return;
} else { } else {
// Failed to load with Imagick, fallback // Failed to load with Imagick, fallback
@ -732,11 +733,6 @@ class Image
*/ */
public function getBlurHash(): string public function getBlurHash(): string
{ {
if ($this->isImagick()) {
// Imagick is not supported
return '';
}
$width = $this->getWidth(); $width = $this->getWidth();
$height = $this->getHeight(); $height = $this->getHeight();
@ -750,10 +746,14 @@ class Image
for ($y = 0; $y < $height; ++$y) { for ($y = 0; $y < $height; ++$y) {
$row = []; $row = [];
for ($x = 0; $x < $width; ++$x) { for ($x = 0; $x < $width; ++$x) {
$index = imagecolorat($this->image, $x, $y); if ($this->isImagick()) {
$colors = imagecolorsforindex($this->image, $index); $colors = $this->image->getImagePixelColor($x, $y)->getColor();
$row[] = [$colors['r'], $colors['g'], $colors['b']];
$row[] = [$colors['red'], $colors['green'], $colors['blue']]; } else {
$index = imagecolorat($this->image, $x, $y);
$colors = @imagecolorsforindex($this->image, $index);
$row[] = [$colors['red'], $colors['green'], $colors['blue']];
}
} }
$pixels[] = $row; $pixels[] = $row;
} }
@ -775,25 +775,37 @@ class Image
*/ */
public function getFromBlurHash(string $blurhash, int $width, int $height) public function getFromBlurHash(string $blurhash, int $width, int $height)
{ {
if ($this->isImagick()) {
// Imagick is not supported
return;
}
$scaled = Images::getScalingDimensions($width, $height, 90); $scaled = Images::getScalingDimensions($width, $height, 90);
$pixels = Blurhash::decode($blurhash, $scaled['width'], $scaled['height']); $pixels = Blurhash::decode($blurhash, $scaled['width'], $scaled['height']);
$this->image = imagecreatetruecolor($scaled['width'], $scaled['height']); if ($this->isImagick()) {
$this->image = new Imagick();
$draw = new ImagickDraw();
$this->image->newImage($scaled['width'], $scaled['height'], '', 'png');
} else {
$this->image = imagecreatetruecolor($scaled['width'], $scaled['height']);
}
for ($y = 0; $y < $scaled['height']; ++$y) { for ($y = 0; $y < $scaled['height']; ++$y) {
for ($x = 0; $x < $scaled['width']; ++$x) { for ($x = 0; $x < $scaled['width']; ++$x) {
[$r, $g, $b] = $pixels[$y][$x]; [$r, $g, $b] = $pixels[$y][$x];
imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b)); if ($this->isImagick()) {
$draw->setFillColor("rgb($r, $g, $b)");
$draw->point($x, $y);
} else {
imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b));
}
} }
} }
$this->width = imagesx($this->image); if ($this->isImagick()) {
$this->height = imagesy($this->image); $this->image->drawImage($draw);
$this->valid = true; } else {
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
}
$this->valid = true;
$this->scaleUp(min($width, $height)); $this->scaleUp(min($width, $height));
} }

View file

@ -34,7 +34,6 @@ use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Conversation; use Friendica\Model\Conversation;
use Friendica\Model\Event; use Friendica\Model\Event;
use Friendica\Model\FContact;
use Friendica\Model\GServer; use Friendica\Model\GServer;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\ItemURI; use Friendica\Model\ItemURI;
@ -45,6 +44,7 @@ use Friendica\Model\Post;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\Tag; use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
@ -981,12 +981,12 @@ class DFRN
} }
} }
$fcontact = FContact::getByURL($contact['addr']); try {
if (empty($fcontact)) { $pubkey = DI::dsprContact()->getByAddr(WebFingerUri::fromString($contact['addr']))->pubKey;
} catch (HTTPException\NotFoundException|\InvalidArgumentException $e) {
Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); Logger::notice('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']);
return -22; return -22;
} }
$pubkey = $fcontact['pubkey'] ?? '';
} else { } else {
$pubkey = ''; $pubkey = '';
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,140 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Entity;
use Psr\Http\Message\UriInterface;
/**
* @property-read $uriId
* @property-read $url
* @property-read $guid
* @property-read $addr
* @property-read $alias
* @property-read $nick
* @property-read $name
* @property-read $givenName
* @property-read $familyName
* @property-read $photo
* @property-read $photoMedium
* @property-read $photoSmall
* @property-read $batch
* @property-read $notify
* @property-read $poll
* @property-read $subscribe
* @property-read $searchable
* @property-read $pubKey
* @property-read $baseurl
* @property-read $gsid
* @property-read $created
* @property-read $updated
* @property-read $interacting_count
* @property-read $interacted_count
* @property-read $post_count
*/
class DiasporaContact extends \Friendica\BaseEntity
{
/** @var int */
protected $uriId;
/** @var UriInterface */
protected $url;
/** @var string */
protected $guid;
/** @var string */
protected $addr;
/** @var UriInterface */
protected $alias;
/** @var string */
protected $nick;
/** @var string */
protected $name;
/** @var string */
protected $givenName;
/** @var string */
protected $familyName;
/** @var UriInterface */
protected $photo;
/** @var UriInterface */
protected $photoMedium;
/** @var UriInterface */
protected $photoSmall;
/** @var UriInterface */
protected $batch;
/** @var UriInterface */
protected $notify;
/** @var UriInterface */
protected $poll;
/** @var UriInterface */
protected $subscribe;
/** @var bool */
protected $searchable;
/** @var string */
protected $pubKey;
/** @var UriInterface */
protected $baseurl;
/** @var int */
protected $gsid;
/** @var \DateTime */
protected $created;
/** @var \DateTime */
protected $updated;
/** @var int */
protected $interacting_count;
/** @var int */
protected $interacted_count;
/** @var int */
protected $post_count;
public function __construct(
UriInterface $url, \DateTime $created, string $guid = null, string $addr = null, UriInterface $alias = null,
string $nick = null, string $name = null, string $givenName = null, string $familyName = null,
UriInterface $photo = null, UriInterface $photoMedium = null, UriInterface $photoSmall = null,
UriInterface $batch = null, UriInterface $notify = null, UriInterface $poll = null, UriInterface $subscribe = null,
bool $searchable = null, string $pubKey = null, UriInterface $baseurl = null, int $gsid = null,
\DateTime $updated = null, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0, int $uriId = null
) {
$this->uriId = $uriId;
$this->url = $url;
$this->guid = $guid;
$this->addr = $addr;
$this->alias = $alias;
$this->nick = $nick;
$this->name = $name;
$this->givenName = $givenName;
$this->familyName = $familyName;
$this->photo = $photo;
$this->photoMedium = $photoMedium;
$this->photoSmall = $photoSmall;
$this->batch = $batch;
$this->notify = $notify;
$this->poll = $poll;
$this->subscribe = $subscribe;
$this->searchable = $searchable;
$this->pubKey = $pubKey;
$this->baseurl = $baseurl;
$this->gsid = $gsid;
$this->created = $created;
$this->updated = $updated;
$this->interacting_count = $interacting_count;
$this->interacted_count = $interacted_count;
$this->post_count = $post_count;
}
}

View file

@ -0,0 +1,102 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Factory;
use Friendica\Capabilities\ICanCreateFromTableRow;
use Friendica\Database\DBA;
use GuzzleHttp\Psr7\Uri;
class DiasporaContact extends \Friendica\BaseFactory implements ICanCreateFromTableRow
{
public function createFromTableRow(array $row): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
{
return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
new Uri($row['url']),
new \DateTime($row['created'], new \DateTimeZone('UTC')),
$row['guid'],
$row['addr'],
$row['alias'] ? new Uri($row['alias']) : null,
$row['nick'],
$row['name'],
$row['given-name'],
$row['family-name'],
$row['photo'] ? new Uri($row['photo']) : null,
$row['photo-medium'] ? new Uri($row['photo-medium']) : null,
$row['photo-small'] ? new Uri($row['photo-small']) : null,
$row['batch'] ? new Uri($row['batch']) : null,
$row['notify'] ? new Uri($row['notify']) : null,
$row['poll'] ? new Uri($row['poll']) : null,
$row['subscribe'] ? new Uri($row['subscribe']) : null,
$row['searchable'],
$row['pubkey'],
$row['baseurl'] ? new Uri($row['baseurl']) : null,
$row['gsid'],
$row['updated'] !== DBA::NULL_DATETIME ? new \DateTime($row['updated'], new \DateTimeZone('UTC')) : null,
$row['interacting_count'],
$row['interacted_count'],
$row['post_count'],
$row['uri-id'],
);
}
/**
* @param array $data Data returned by \Friendica\Network\Probe::uri()
* @param int $uriId The URI ID of the Diaspora contact URL + GUID
* @param \DateTime $created
* @param int $interacting_count
* @param int $interacted_count
* @param int $post_count
* @return \Friendica\Protocol\Diaspora\Entity\DiasporaContact
*/
public function createfromProbeData(array $data, int $uriId, \DateTime $created, int $interacting_count = 0, int $interacted_count = 0, int $post_count = 0): \Friendica\Protocol\Diaspora\Entity\DiasporaContact
{
$alias = $data['alias'] != $data['url'] ? $data['alias'] : null;
return new \Friendica\Protocol\Diaspora\Entity\DiasporaContact(
new Uri($data['url']),
$created,
$data['guid'],
$data['addr'],
$alias ? new Uri($alias) : null,
$data['nick'],
$data['name'],
$data['given-name'] ?? '',
$data['family-name'] ?? '',
$data['photo'] ? new Uri($data['photo']) : null,
!empty($data['photo_medium']) ? new Uri($data['photo_medium']) : null,
!empty($data['photo_small']) ? new Uri($data['photo_small']) : null,
$data['batch'] ? new Uri($data['batch']) : null,
$data['notify'] ? new Uri($data['notify']) : null,
$data['poll'] ? new Uri($data['poll']) : null,
$data['subscribe'] ? new Uri($data['subscribe']) : null,
!$data['hide'],
$data['pubkey'],
$data['baseurl'] ? new Uri($data['baseurl']) : null,
$data['gsid'],
null,
$interacting_count,
$interacted_count,
$post_count,
$uriId,
);
}
}

View file

@ -0,0 +1,283 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol\Diaspora\Repository;
use Friendica\BaseRepository;
use Friendica\Core\System;
use Friendica\Database\Database;
use Friendica\Database\Definition\DbaDefinition;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Network\HTTPException;
use Friendica\Protocol\Diaspora\Entity;
use Friendica\Protocol\Diaspora\Factory;
use Friendica\Protocol\WebFingerUri;
use Friendica\Util\DateTimeFormat;
use Psr\Http\Message\UriInterface;
use Psr\Log\LoggerInterface;
class DiasporaContact extends BaseRepository
{
const ALWAYS_UPDATE = true;
const NEVER_UPDATE = false;
const UPDATE_IF_MISSING_OR_OUTDATED = null;
protected static $table_name = 'diaspora-contact-view';
/** @var Factory\DiasporaContact */
protected $factory;
/** @var DbaDefinition */
private $definition;
public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory)
{
parent::__construct($database, $logger, $factory);
$this->definition = $definition;
}
/**
* @param array $condition
* @param array $params
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOne(array $condition, array $params = []): Entity\DiasporaContact
{
return parent::_selectOne($condition, $params);
}
/**
* @param int $uriId
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByUriId(int $uriId): Entity\DiasporaContact
{
return $this->selectOne(['uri-id' => $uriId]);
}
/**
* @param UriInterface $uri
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact
{
try {
return $this->selectOne(['url' => (string) $uri]);
} catch (HTTPException\NotFoundException $e) {
}
try {
return $this->selectOne(['addr' => (string) $uri]);
} catch (HTTPException\NotFoundException $e) {
}
return $this->selectOne(['alias' => (string) $uri]);
}
/**
* @param WebFingerUri $uri
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact
{
return $this->selectOne(['addr' => $uri->getAddr()]);
}
/**
* @param int $uriId
* @return bool
* @throws \Exception
*/
public function existsByUriId(int $uriId): bool
{
return $this->db->exists(self::$table_name, ['uri-id' => $uriId]);
}
public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact
{
$uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]);
$fields = [
'uri-id' => $uriId,
'addr' => $DiasporaContact->addr,
'alias' => (string)$DiasporaContact->alias,
'nick' => $DiasporaContact->nick,
'name' => $DiasporaContact->name,
'given-name' => $DiasporaContact->givenName,
'family-name' => $DiasporaContact->familyName,
'photo' => (string)$DiasporaContact->photo,
'photo-medium' => (string)$DiasporaContact->photoMedium,
'photo-small' => (string)$DiasporaContact->photoSmall,
'batch' => (string)$DiasporaContact->batch,
'notify' => (string)$DiasporaContact->notify,
'poll' => (string)$DiasporaContact->poll,
'subscribe' => (string)$DiasporaContact->subscribe,
'searchable' => $DiasporaContact->searchable,
'pubkey' => $DiasporaContact->pubKey,
'gsid' => $DiasporaContact->gsid,
'created' => $DiasporaContact->created->format(DateTimeFormat::MYSQL),
'updated' => DateTimeFormat::utcNow(),
'interacting_count' => $DiasporaContact->interacting_count,
'interacted_count' => $DiasporaContact->interacted_count,
'post_count' => $DiasporaContact->post_count,
];
// Limit the length on incoming fields
$fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields);
$this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE);
return $this->selectOneByUriId($uriId);
}
/**
* Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode
*
* @param WebFingerUri $uri Profile address
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
{
if ($update !== self::ALWAYS_UPDATE) {
try {
$dcontact = $this->selectOneByAddr($uri);
if ($update === self::NEVER_UPDATE) {
return $dcontact;
}
} catch (HTTPException\NotFoundException $e) {
if ($update === self::NEVER_UPDATE) {
throw $e;
}
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
// but we still need the result of a probe to create the missing diaspora-contact record.
$update = self::ALWAYS_UPDATE;
}
}
$contact = Contact::getByURL($uri, $update, ['uri-id']);
if (empty($contact['uri-id'])) {
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
}
return self::selectOneByUriId($contact['uri-id']);
}
/**
* Fetch a Diaspora profile from a given profile URL and updates it depending on the mode
*
* @param UriInterface $uri Profile URL
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
* @return Entity\DiasporaContact
* @throws HTTPException\NotFoundException
*/
public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
{
if ($update !== self::ALWAYS_UPDATE) {
try {
$dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri));
if ($update === self::NEVER_UPDATE) {
return $dcontact;
}
} catch (HTTPException\NotFoundException $e) {
if ($update === self::NEVER_UPDATE) {
throw $e;
}
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
// but we still need the result of a probe to create the missing diaspora-contact record.
$update = self::ALWAYS_UPDATE;
}
}
$contact = Contact::getByURL($uri, $update, ['uri-id']);
if (empty($contact['uri-id'])) {
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
}
return self::selectOneByUriId($contact['uri-id']);
}
/**
* Update or create a diaspora-contact entry via a probe array
*
* @param array $data Probe array
* @return Entity\DiasporaContact
* @throws \Exception
*/
public function updateFromProbeArray(array $data): Entity\DiasporaContact
{
$uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]);
$contact = Contact::getByUriId($uriId, ['id', 'created']);
$apcontact = APContact::getByURL($data['url'], false);
if (!empty($apcontact)) {
$interacting_count = $apcontact['followers_count'];
$interacted_count = $apcontact['following_count'];
$post_count = $apcontact['statuses_count'];
} elseif (!empty($contact['id'])) {
$last_interaction = DateTimeFormat::utc('now - 180 days');
$interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$interacted_count = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$post_count = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
}
$DiasporaContact = $this->factory->createfromProbeData(
$data,
$uriId,
new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')),
$interacting_count ?? 0,
$interacted_count ?? 0,
$post_count ?? 0
);
$DiasporaContact = $this->save($DiasporaContact);
$this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]);
return $DiasporaContact;
}
/**
* get a url (scheme://domain.tld/u/user) from a given contact guid
*
* @param mixed $guid Hexadecimal string guid
*
* @return string the contact url or null
* @throws \Exception
*/
public function getUrlByGuid(string $guid): ?string
{
$diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]);
return $diasporaContact['url'] ?? null;
}
}

View file

@ -0,0 +1,113 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Protocol;
use GuzzleHttp\Psr7\Uri;
class WebFingerUri
{
/**
* @var string
*/
private $user;
/**
* @var string
*/
private $host;
/**
* @var int|null
*/
private $port;
/**
* @var string|null
*/
private $path;
private function __construct(string $user, string $host, int $port = null, string $path = null)
{
$this->user = $user;
$this->host = $host;
$this->port = $port;
$this->path = $path;
$this->validate();
}
/**
* @param string $addr
* @return WebFingerUri
*/
public static function fromString(string $addr): WebFingerUri
{
$uri = new Uri('acct://' . preg_replace('/^acct:/', '', $addr));
return new self($uri->getUserInfo(), $uri->getHost(), $uri->getPort(), $uri->getPath());
}
private function validate()
{
if (!$this->user) {
throw new \InvalidArgumentException('WebFinger URI User part is required');
}
if (!$this->host) {
throw new \InvalidArgumentException('WebFinger URI Host part is required');
}
}
public function getUser(): string
{
return $this->user;
}
public function getHost(): string
{
return $this->host;
}
public function getFullHost(): string
{
return $this->host
. ($this->port ? ':' . $this->port : '') .
($this->path ?: '');
}
public function getLongForm(): string
{
return 'acct:' . $this->getShortForm();
}
public function getShortForm(): string
{
return $this->user . '@' . $this->getFullHost();
}
public function getAddr(): string
{
return $this->getShortForm();
}
public function __toString(): string
{
return $this->getShortForm();
}
}

View file

@ -29,7 +29,6 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\FContact;
use Friendica\Model\GServer; use Friendica\Model\GServer;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
@ -94,7 +93,7 @@ class Delivery
if ($item['verb'] == Activity::ANNOUNCE) { if ($item['verb'] == Activity::ANNOUNCE) {
continue; continue;
} }
if ($item['id'] == $parent_id) { if ($item['id'] == $parent_id) {
$parent = $item; $parent = $item;
} }
@ -278,7 +277,7 @@ class Delivery
private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null) private static function deliverDFRN(string $cmd, array $contact, array $owner, array $items, array $target_item, bool $public_message, bool $top_level, bool $followup, int $server_protocol = null)
{ {
// Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora // Transmit Diaspora reshares via Diaspora if the Friendica contact support Diaspora
if (Diaspora::getReshareDetails($target_item ?? []) && !empty(FContact::getByURL($contact['addr'], false))) { if (Diaspora::getReshareDetails($target_item ?? []) && Diaspora::isSupportedByContactUrl($contact['addr'], false)) {
Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]); Logger::info('Reshare will be transmitted via Diaspora', ['url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id']]);
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
return; return;

View file

@ -189,7 +189,7 @@ class ExpirePosts
AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `user-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `fcontact` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `diaspora-contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-id` = `item-uri`.`id`)

View file

@ -1,41 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Worker;
use Friendica\Core\Logger;
use Friendica\Model\FContact;
class UpdateFContact
{
/**
* Update fcontact data via probe
*
* @param string $handle Contact handle
* @return void
*/
public static function execute(string $handle)
{
$success = FContact::getByURL($handle, true);
Logger::info('Updated from probe', ['handle' => $handle, 'success' => $success]);
}
}

View file

@ -55,7 +55,7 @@
use Friendica\Database\DBA; use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1497); define('DB_UPDATE_VERSION', 1500);
} }
return [ return [
@ -637,6 +637,39 @@ return [
"wid" => ["wid"], "wid" => ["wid"],
] ]
], ],
"diaspora-contact" => [
"comment" => "Diaspora compatible contacts - used in the Diaspora implementation",
"fields" => [
"uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact URL"],
"addr" => ["type" => "varchar(255)", "comment" => ""],
"alias" => ["type" => "varchar(255)", "comment" => ""],
"nick" => ["type" => "varchar(255)", "comment" => ""],
"name" => ["type" => "varchar(255)", "comment" => ""],
"given-name" => ["type" => "varchar(255)", "comment" => ""],
"family-name" => ["type" => "varchar(255)", "comment" => ""],
"photo" => ["type" => "varchar(255)", "comment" => ""],
"photo-medium" => ["type" => "varchar(255)", "comment" => ""],
"photo-small" => ["type" => "varchar(255)", "comment" => ""],
"batch" => ["type" => "varchar(255)", "comment" => ""],
"notify" => ["type" => "varchar(255)", "comment" => ""],
"poll" => ["type" => "varchar(255)", "comment" => ""],
"subscribe" => ["type" => "varchar(255)", "comment" => ""],
"searchable" => ["type" => "boolean", "comment" => ""],
"pubkey" => ["type" => "text", "comment" => ""],
"gsid" => ["type" => "int unsigned", "foreign" => ["gserver" => "id", "on delete" => "restrict"], "comment" => "Global Server ID"],
"created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"interacting_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts this contact interactes with"],
"interacted_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts that interacted with this contact"],
"post_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of posts and comments"],
],
"indexes" => [
"PRIMARY" => ["uri-id"],
"addr" => ["UNIQUE", "addr"],
"alias" => ["alias"],
"gsid" => ["gsid"],
]
],
"diaspora-interaction" => [ "diaspora-interaction" => [
"comment" => "Signed Diaspora Interaction", "comment" => "Signed Diaspora Interaction",
"fields" => [ "fields" => [
@ -690,39 +723,6 @@ return [
"uri-id" => ["uri-id"], "uri-id" => ["uri-id"],
] ]
], ],
"fcontact" => [
"comment" => "Diaspora compatible contacts - used in the Diaspora implementation",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"guid" => ["type" => "varbinary(255)", "not null" => "1", "default" => "", "comment" => "unique id"],
"url" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the fcontact url"],
"name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"photo" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"request" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"nick" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"addr" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"batch" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"notify" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"poll" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"confirm" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => ""],
"alias" => ["type" => "varbinary(383)", "not null" => "1", "default" => "", "comment" => ""],
"pubkey" => ["type" => "text", "comment" => ""],
"created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"interacting_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts this contact interactes with"],
"interacted_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of contacts that interacted with this contact"],
"post_count" => ["type" => "int unsigned", "default" => 0, "comment" => "Number of posts and comments"],
],
"indexes" => [
"PRIMARY" => ["id"],
"addr" => ["addr(32)"],
"url" => ["UNIQUE", "url(190)"],
"uri-id" => ["UNIQUE", "uri-id"],
]
],
"fetch-entry" => [ "fetch-entry" => [
"comment" => "", "comment" => "",
"fields" => [ "fields" => [
@ -870,7 +870,7 @@ return [
"fields" => [ "fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "User id"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "User id"],
"fid" => ["type" => "int unsigned", "relation" => ["fcontact" => "id"], "comment" => "deprecated"], "fid" => ["type" => "int unsigned", "comment" => "deprecated"],
"contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => ""], "contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => ""],
"suggest-cid" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Suggested contact"], "suggest-cid" => ["type" => "int unsigned", "foreign" => ["contact" => "id"], "comment" => "Suggested contact"],
"knowyou" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "knowyou" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],

View file

@ -155,7 +155,6 @@
"author-hidden" => ["author", "hidden"], "author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"], "author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"], "author-gsid" => ["author", "gsid"],
"author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-user", "owner-id"], "owner-id" => ["post-user", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"], "owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"], "owner-link" => ["owner", "url"],
@ -332,7 +331,6 @@
"author-hidden" => ["author", "hidden"], "author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"], "author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"], "author-gsid" => ["author", "gsid"],
"author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-thread-user", "owner-id"], "owner-id" => ["post-thread-user", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"], "owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"], "owner-link" => ["owner", "url"],
@ -495,7 +493,6 @@
"author-hidden" => ["author", "hidden"], "author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"], "author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"], "author-gsid" => ["author", "gsid"],
"author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post", "owner-id"], "owner-id" => ["post", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"], "owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"], "owner-link" => ["owner", "url"],
@ -634,7 +631,6 @@
"author-hidden" => ["author", "hidden"], "author-hidden" => ["author", "hidden"],
"author-updated" => ["author", "updated"], "author-updated" => ["author", "updated"],
"author-gsid" => ["author", "gsid"], "author-gsid" => ["author", "gsid"],
"author-uri-id" => ["author", "uri-id"],
"owner-id" => ["post-thread", "owner-id"], "owner-id" => ["post-thread", "owner-id"],
"owner-uri-id" => ["owner", "uri-id"], "owner-uri-id" => ["owner", "uri-id"],
"owner-link" => ["owner", "url"], "owner-link" => ["owner", "url"],
@ -994,11 +990,11 @@
"blocked" => ["contact", "blocked"], "blocked" => ["contact", "blocked"],
"dfrn-notify" => ["contact", "notify"], "dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"], "dfrn-poll" => ["contact", "poll"],
"diaspora-guid" => ["fcontact", "guid"], "diaspora-guid" => ["item-uri", "guid"],
"diaspora-batch" => ["fcontact", "batch"], "diaspora-batch" => ["diaspora-contact", "batch"],
"diaspora-notify" => ["fcontact", "notify"], "diaspora-notify" => ["diaspora-contact", "notify"],
"diaspora-poll" => ["fcontact", "poll"], "diaspora-poll" => ["diaspora-contact", "poll"],
"diaspora-alias" => ["fcontact", "alias"], "diaspora-alias" => ["diaspora-contact", "alias"],
"ap-uuid" => ["apcontact", "uuid"], "ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"], "ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"], "ap-following" => ["apcontact", "following"],
@ -1017,9 +1013,9 @@
"query" => "FROM `contact` "query" => "FROM `contact`
LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `contact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id` LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = contact.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid` LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`
WHERE `contact`.`uid` = 0" WHERE `contact`.`uid` = 0"
], ],
"account-user-view" => [ "account-user-view" => [
"fields" => [ "fields" => [
@ -1093,14 +1089,14 @@
"reason" => ["ucontact", "reason"], "reason" => ["ucontact", "reason"],
"dfrn-notify" => ["contact", "notify"], "dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"], "dfrn-poll" => ["contact", "poll"],
"diaspora-guid" => ["fcontact", "guid"], "diaspora-guid" => ["item-uri", "guid"],
"diaspora-batch" => ["fcontact", "batch"], "diaspora-batch" => ["diaspora-contact", "batch"],
"diaspora-notify" => ["fcontact", "notify"], "diaspora-notify" => ["diaspora-contact", "notify"],
"diaspora-poll" => ["fcontact", "poll"], "diaspora-poll" => ["diaspora-contact", "poll"],
"diaspora-alias" => ["fcontact", "alias"], "diaspora-alias" => ["diaspora-contact", "alias"],
"diaspora-interacting_count" => ["fcontact", "interacting_count"], "diaspora-interacting_count" => ["diaspora-contact", "interacting_count"],
"diaspora-interacted_count" => ["fcontact", "interacted_count"], "diaspora-interacted_count" => ["diaspora-contact", "interacted_count"],
"diaspora-post_count" => ["fcontact", "post_count"], "diaspora-post_count" => ["diaspora-contact", "post_count"],
"ap-uuid" => ["apcontact", "uuid"], "ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"], "ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"], "ap-following" => ["apcontact", "following"],
@ -1120,7 +1116,7 @@
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0 INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id` LEFT JOIN `item-uri` ON `item-uri`.`id` = `ucontact`.`uri-id`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id` LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr' LEFT JOIN `diaspora-contact` ON `diaspora-contact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`" LEFT JOIN `gserver` ON `gserver`.`id` = contact.`gsid`"
], ],
"pending-view" => [ "pending-view" => [
@ -1190,5 +1186,36 @@
"query" => "FROM `profile_field` "query" => "FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`" INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`"
], ],
"diaspora-contact-view" => [
"fields" => [
"uri-id" => ["diaspora-contact", "uri-id"],
"url" => ["item-uri", "uri"],
"guid" => ["item-uri", "guid"],
"addr" => ["diaspora-contact", "addr"],
"alias" => ["diaspora-contact", "alias"],
"nick" => ["diaspora-contact", "nick"],
"name" => ["diaspora-contact", "name"],
"given-name" => ["diaspora-contact", "given-name"],
"family-name" => ["diaspora-contact", "family-name"],
"photo" => ["diaspora-contact", "photo"],
"photo-medium" => ["diaspora-contact", "photo-medium"],
"photo-small" => ["diaspora-contact", "photo-small"],
"batch" => ["diaspora-contact", "batch"],
"notify" => ["diaspora-contact", "notify"],
"poll" => ["diaspora-contact", "poll"],
"subscribe" => ["diaspora-contact", "subscribe"],
"searchable" => ["diaspora-contact", "searchable"],
"pubkey" => ["diaspora-contact", "pubkey"],
"baseurl" => ["gserver", "url"],
"gsid" => ["diaspora-contact", "gsid"],
"created" => ["diaspora-contact", "created"],
"updated" => ["diaspora-contact", "updated"],
"interacting_count" => ["diaspora-contact", "interacting_count"],
"interacted_count" => ["diaspora-contact", "interacted_count"],
"post_count" => ["diaspora-contact", "post_count"],
],
"query" => "FROM `diaspora-contact`
INNER JOIN `item-uri` ON `item-uri`.`id` = `diaspora-contact`.`uri-id`
LEFT JOIN `gserver` ON `gserver`.`id` = `diaspora-contact`.`gsid`"
],
]; ];

View file

@ -0,0 +1,135 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Main database structure configuration file.
*
* Here are described all the tables, fields and indexes Friendica needs to work.
* The entry order is mostly alphabetic - with the exception of tables that are used in foreign keys.
*
* Syntax (braces indicate optionale values):
* "<table name>" => [
* "comment" => "Description of the table",
* "fields" => [
* "<field name>" => [
* "type" => "<field type>{(<field size>)} <unsigned>",
* "not null" => 0|1,
* {"extra" => "auto_increment",}
* {"default" => "<default value>",}
* {"default" => NULL_DATE,} (for datetime fields)
* {"primary" => "1",}
* {"foreign|relation" => ["<foreign key table name>" => "<foreign key field name>"],}
* "comment" => "Description of the fields"
* ],
* ...
* ],
* "indexes" => [
* "PRIMARY" => ["<primary key field name>", ...],
* "<index name>" => [{"UNIQUE",} "<field name>{(<key size>)}", ...]
* ...
* ],
* ],
*
* Whenever possible prefer "foreign" before "relation" with the foreign keys.
* "foreign" adds true foreign keys on the database level, while "relation" is just an indicator of a table relation without any consequences
*
* If you need to make any change, make sure to increment the DB_UPDATE_VERSION constant value below.
*
*/
namespace Friendica\Test\src\Protocol;
use Friendica\Protocol\WebFingerUri;
use PHPUnit\Framework\TestCase;
class WebFingerUriTest extends TestCase
{
public function dataFromString(): array
{
return [
'long' => [
'expectedLong' => 'acct:selma@www.example.com:8080/friend',
'expectedShort' => 'selma@www.example.com:8080/friend',
'input' => 'acct:selma@www.example.com:8080/friend',
],
'short' => [
'expectedLong' => 'acct:selma@www.example.com:8080/friend',
'expectedShort' => 'selma@www.example.com:8080/friend',
'input' => 'selma@www.example.com:8080/friend',
],
'minimal' => [
'expectedLong' => 'acct:bob@example.com',
'expectedShort' => 'bob@example.com',
'input' => 'bob@example.com',
],
'acct:' => [
'expectedLong' => 'acct:alice@example.acct:90',
'expectedShort' => 'alice@example.acct:90',
'input' => 'alice@example.acct:90',
],
];
}
/**
* @dataProvider dataFromString
* @param string $expectedLong
* @param string $expectedShort
* @param string $input
* @return void
*/
public function testFromString(string $expectedLong, string $expectedShort, string $input)
{
$uri = WebFingerUri::fromString($input);
$this->assertEquals($expectedLong, $uri->getLongForm());
$this->assertEquals($expectedShort, $uri->getShortForm());
}
public function dataFromStringFailure()
{
return [
'missing user' => [
'input' => 'example.com',
],
'missing user @' => [
'input' => '@example.com',
],
'missing host' => [
'input' => 'alice',
],
'missing host @' => [
'input' => 'alice@',
],
'missing everything' => [
'input' => '',
],
];
}
/**
* @dataProvider dataFromStringFailure
* @param string $input
* @return void
*/
public function testFromStringFailure(string $input)
{
$this->expectException(\InvalidArgumentException::class);
WebFingerUri::fromString($input);
}
}

View file

@ -974,7 +974,7 @@ function update_1429()
return Update::FAILED; return Update::FAILED;
} }
if (!DBA::e("UPDATE `fcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) { if (DBStructure::existsTable('fcontact') && !DBA::e("UPDATE `fcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
return Update::FAILED; return Update::FAILED;
} }
@ -1013,6 +1013,10 @@ function update_1438()
function update_1439() function update_1439()
{ {
if (!DBStructure::existsTable('fcontact')) {
return Update::SUCCESS;
}
$intros = DBA::select('intro', ['id', 'fid'], ["NOT `fid` IS NULL AND `fid` != ?", 0]); $intros = DBA::select('intro', ['id', 'fid'], ["NOT `fid` IS NULL AND `fid` != ?", 0]);
while ($intro = DBA::fetch($intros)) { while ($intro = DBA::fetch($intros)) {
$fcontact = DBA::selectFirst('fcontact', ['url'], ['id' => $intro['fid']]); $fcontact = DBA::selectFirst('fcontact', ['url'], ['id' => $intro['fid']]);
@ -1024,6 +1028,8 @@ function update_1439()
} }
} }
DBA::close($intros); DBA::close($intros);
return Update::SUCCESS;
} }
function update_1440() function update_1440()