Merge remote-tracking branch 'upstream/develop' into contact-media

This commit is contained in:
Michael 2021-10-02 15:13:55 +00:00
commit a44d200c21
35 changed files with 702 additions and 433 deletions

View file

@ -1,9 +1,20 @@
#!/bin/bash #!/bin/bash
FULLPATH=$(dirname $(readlink -f "$0")) set -eo pipefail
if [ "$1" == "--help" -o "$1" == "-h" ] function resolve {
if [ "$(uname)" == "Darwin" ]
then
realpath "$1"
else
readlink -f "$1"
fi
}
FULLPATH=$(dirname "$(resolve "$0")")
if [ "$1" == "--help" ] || [ "$1" == "-h" ]
then then
echo "$(basename $(readlink -f "$0")) [options]" echo "$(basename "$(resolve "$0")") [options]"
echo echo
echo "-a | --addon <name> extract strings from addon 'name'" echo "-a | --addon <name> extract strings from addon 'name'"
echo "-s | --single single addon mode: extract string from current folder" echo "-s | --single single addon mode: extract string from current folder"
@ -12,20 +23,19 @@ fi
MODE='default' MODE='default'
ADDONNAME= ADDONNAME=
if [ "$1" == "--addon" -o "$1" == "-a" ] if [ "$1" == "--addon" ] || [ "$1" == "-a" ]
then then
MODE='addon' MODE='addon'
if [ -z $2 ]; then echo -e "ERROR: missing addon name\n\nrun_xgettext.sh -a <addonname>"; exit 1; fi if [ -z "$2" ]; then echo -e "ERROR: missing addon name\n\nrun_xgettext.sh -a <addonname>"; exit 1; fi
ADDONNAME=$2 ADDONNAME=$2
if [ ! -d "$FULLPATH/../addon/$ADDONNAME" ]; then echo "ERROR: addon '$ADDONNAME' not found"; exit 2; fi if [ ! -d "$FULLPATH/../addon/$ADDONNAME" ]; then echo "ERROR: addon '$ADDONNAME' not found"; exit 2; fi
fi fi
if [ "$1" == "--single" -o "$1" == "-s" ] if [ "$1" == "--single" ] || [ "$1" == "-s" ]
then then
MODE='single' MODE='single'
fi fi
case "$MODE" in case "$MODE" in
'addon') 'addon')
cd "$FULLPATH/../addon/$ADDONNAME" cd "$FULLPATH/../addon/$ADDONNAME"
@ -36,7 +46,7 @@ case "$MODE" in
;; ;;
'single') 'single')
FULLPATH=$PWD FULLPATH=$PWD
ADDONNAME=$(basename $FULLPATH) ADDONNAME=$(basename "$FULLPATH")
mkdir -p "$FULLPATH/lang/C" mkdir -p "$FULLPATH/lang/C"
OUTFILE="$FULLPATH/lang/C/messages.po" OUTFILE="$FULLPATH/lang/C/messages.po"
FINDSTARTDIR="." FINDSTARTDIR="."
@ -61,51 +71,56 @@ KEYWORDS="-k -kt -ktt:1,2"
echo "Extract strings to $OUTFILE.." echo "Extract strings to $OUTFILE.."
rm "$OUTFILE"; touch "$OUTFILE" rm "$OUTFILE"; touch "$OUTFILE"
# shellcheck disable=SC2086 # $FINDOPTS is meant to be split
find_result=$(find "$FINDSTARTDIR" $FINDOPTS -name "*.php" -type f | LC_ALL=C sort --stable) find_result=$(find "$FINDSTARTDIR" $FINDOPTS -name "*.php" -type f | LC_ALL=C sort --stable)
total_files=$(wc -l <<< "${find_result}") total_files=$(wc -l <<< "${find_result}")
count=1
for file in $find_result for file in $find_result
do do
((count++))
echo -ne " \r" echo -ne " \r"
echo -ne "Reading file $count/$total_files..." echo -ne "Reading file $count/$total_files..."
# On Windows, find still outputs the name of pruned folders # On Windows, find still outputs the name of pruned folders
if [ ! -d "$file" ] if [ ! -d "$file" ]
then then
# shellcheck disable=SC2086 # $KEYWORDS is meant to be split
xgettext $KEYWORDS -j -o "$OUTFILE" --from-code=UTF-8 "$file" || exit 1 xgettext $KEYWORDS -j -o "$OUTFILE" --from-code=UTF-8 "$file" || exit 1
sed -i "s/CHARSET/UTF-8/g" "$OUTFILE" sed -i.bkp "s/CHARSET/UTF-8/g" "$OUTFILE"
fi fi
(( count++ ))
done done
echo -ne "\n" echo -ne "\n"
echo "Interpolate metadata.." echo "Interpolate metadata.."
sed -i "s/^\"Plural-Forms.*$//g" "$OUTFILE" sed -i.bkp "s/^\"Plural-Forms.*$//g" "$OUTFILE"
case "$MODE" in case "$MODE" in
'addon'|'single') 'addon'|'single')
sed -i "s/SOME DESCRIPTIVE TITLE./ADDON $ADDONNAME/g" "$OUTFILE" sed -i.bkp "s/SOME DESCRIPTIVE TITLE./ADDON $ADDONNAME/g" "$OUTFILE"
sed -i "s/YEAR THE PACKAGE'S COPYRIGHT HOLDER//g" "$OUTFILE" sed -i.bkp "s/YEAR THE PACKAGE'S COPYRIGHT HOLDER//g" "$OUTFILE"
sed -i "s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.//g" "$OUTFILE" sed -i.bkp "s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.//g" "$OUTFILE"
sed -i "s/PACKAGE VERSION//g" "$OUTFILE" sed -i.bkp "s/PACKAGE VERSION//g" "$OUTFILE"
sed -i "s/PACKAGE/Friendica $ADDONNAME addon/g" "$OUTFILE" sed -i.bkp "s/PACKAGE/Friendica $ADDONNAME addon/g" "$OUTFILE"
;; ;;
'default') 'default')
sed -i "s/SOME DESCRIPTIVE TITLE./FRIENDICA Distributed Social Network/g" "$OUTFILE" sed -i.bkp "s/SOME DESCRIPTIVE TITLE./FRIENDICA Distributed Social Network/g" "$OUTFILE"
sed -i "s/YEAR THE PACKAGE'S COPYRIGHT HOLDER/2010-$(date +%Y), the Friendica project/g" "$OUTFILE" sed -i.bkp "s/YEAR THE PACKAGE'S COPYRIGHT HOLDER/2010-$(date +%Y), the Friendica project/g" "$OUTFILE"
sed -i "s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR./Mike Macgirvin, 2010/g" "$OUTFILE" sed -i.bkp "s/FIRST AUTHOR <EMAIL@ADDRESS>, YEAR./Mike Macgirvin, 2010/g" "$OUTFILE"
sed -i "s/PACKAGE VERSION/$F9KVERSION/g" "$OUTFILE" sed -i.bkp "s/PACKAGE VERSION/$F9KVERSION/g" "$OUTFILE"
sed -i "s/PACKAGE/Friendica/g" "$OUTFILE" sed -i.bkp "s/PACKAGE/Friendica/g" "$OUTFILE"
;; ;;
esac esac
if [ "" != "$1" -a "$MODE" == "default" ] if [ "" != "$1" ] && [ "$MODE" == "default" ]
then then
UPDATEFILE="$(readlink -f ${FULLPATH}/$1)" UPDATEFILE="$(resolve "${FULLPATH}/$1")"
echo "Merging new strings to $UPDATEFILE.." echo "Merging new strings to $UPDATEFILE.."
msgmerge -U $OUTFILE $UPDATEFILE msgmerge -U "$OUTFILE" "$UPDATEFILE"
fi fi
[ -f "$OUTFILE.bkp" ] && rm "$OUTFILE.bkp"
echo "Done." echo "Done."

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2021.09-dev (Siberian Iris) -- Friendica 2021.12-dev (Siberian Iris)
-- DB_UPDATE_VERSION 1434 -- DB_UPDATE_VERSION 1435
-- ------------------------------------------ -- ------------------------------------------
@ -114,29 +114,16 @@ CREATE TABLE IF NOT EXISTS `contact` (
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last contact update', `updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last contact update',
`self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self',
`remote_self` boolean NOT NULL DEFAULT '0' COMMENT '',
`rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The kind of the relation between the user and the contact',
`duplex` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`network` char(4) NOT NULL DEFAULT '' COMMENT 'Network of the contact', `network` char(4) NOT NULL DEFAULT '' COMMENT 'Network of the contact',
`protocol` char(4) NOT NULL DEFAULT '' COMMENT 'Protocol of the contact',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by', `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by',
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact', `nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact',
`location` varchar(255) DEFAULT '' COMMENT '', `location` varchar(255) DEFAULT '' COMMENT '',
`about` text COMMENT '', `about` text COMMENT '',
`keywords` text COMMENT 'public keywords (interests) of the contact', `keywords` text COMMENT 'public keywords (interests) of the contact',
`gender` varchar(32) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT 'XMPP address', `xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT 'XMPP address',
`matrix` varchar(255) NOT NULL DEFAULT '' COMMENT 'Matrix address', `matrix` varchar(255) NOT NULL DEFAULT '' COMMENT 'Matrix address',
`attag` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '', `avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`photo` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo of the contact',
`thumb` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (thumb size)',
`micro` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (micro size)',
`header` varchar(255) COMMENT 'Header picture', `header` varchar(255) COMMENT 'Header picture',
`site-pubkey` text COMMENT 'Deprecated',
`issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '', `url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '', `nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url', `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url',
@ -145,54 +132,67 @@ CREATE TABLE IF NOT EXISTS `contact` (
`pubkey` text COMMENT 'RSA public key 4096 bit', `pubkey` text COMMENT 'RSA public key 4096 bit',
`prvkey` text COMMENT 'RSA private key 4096 bit', `prvkey` text COMMENT 'RSA private key 4096 bit',
`batch` varchar(255) NOT NULL DEFAULT '' COMMENT '', `batch` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`request` varchar(255) COMMENT '',
`notify` varchar(255) COMMENT '', `notify` varchar(255) COMMENT '',
`poll` varchar(255) COMMENT '', `poll` varchar(255) COMMENT '',
`confirm` varchar(255) COMMENT '',
`subscribe` varchar(255) COMMENT '', `subscribe` varchar(255) COMMENT '',
`poco` varchar(255) COMMENT '',
`aes_allow` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`ret-aes` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`usehub` boolean NOT NULL DEFAULT '0' COMMENT '',
`subhub` boolean NOT NULL DEFAULT '0' COMMENT '',
`hub-verify` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info', `last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info',
`success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update', `success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update',
`failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update', `failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update',
`failed` boolean COMMENT 'Connection failed', `failed` boolean COMMENT 'Connection failed',
`name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post', `last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post',
`last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery', `last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery',
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status', `blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status',
`block_reason` text COMMENT 'Node-wide block reason', `block_reason` text COMMENT 'Node-wide block reason',
`readonly` boolean NOT NULL DEFAULT '0' COMMENT 'posts of the contact are readonly', `readonly` boolean NOT NULL DEFAULT '0' COMMENT 'posts of the contact are readonly',
`writable` boolean NOT NULL DEFAULT '0' COMMENT '', `contact-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Person, organisation, news, community, relay',
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum', `manually-approve` boolean COMMENT 'Contact requests have to be approved manually',
`prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group',
`contact-type` tinyint NOT NULL DEFAULT 0 COMMENT '',
`manually-approve` boolean COMMENT '',
`hidden` boolean NOT NULL DEFAULT '0' COMMENT '',
`archive` boolean NOT NULL DEFAULT '0' COMMENT '', `archive` boolean NOT NULL DEFAULT '0' COMMENT '',
`pending` boolean NOT NULL DEFAULT '1' COMMENT '',
`deleted` boolean NOT NULL DEFAULT '0' COMMENT 'Contact has been deleted',
`rating` tinyint NOT NULL DEFAULT 0 COMMENT '',
`unsearchable` boolean NOT NULL DEFAULT '0' COMMENT 'Contact prefers to not be searchable', `unsearchable` boolean NOT NULL DEFAULT '0' COMMENT 'Contact prefers to not be searchable',
`sensitive` boolean NOT NULL DEFAULT '0' COMMENT 'Contact posts sensitive content', `sensitive` boolean NOT NULL DEFAULT '0' COMMENT 'Contact posts sensitive content',
`baseurl` varchar(255) DEFAULT '' COMMENT 'baseurl of the contact', `baseurl` varchar(255) DEFAULT '' COMMENT 'baseurl of the contact',
`gsid` int unsigned COMMENT 'Global Server ID', `gsid` int unsigned COMMENT 'Global Server ID',
`reason` text COMMENT '',
`closeness` tinyint unsigned NOT NULL DEFAULT 99 COMMENT '',
`info` mediumtext COMMENT '',
`profile-id` int unsigned COMMENT 'Deprecated',
`bdyear` varchar(4) NOT NULL DEFAULT '' COMMENT '',
`bd` date NOT NULL DEFAULT '0001-01-01' COMMENT '', `bd` date NOT NULL DEFAULT '0001-01-01' COMMENT '',
`reason` text COMMENT '',
`self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self',
`remote_self` boolean NOT NULL DEFAULT '0' COMMENT '',
`rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The kind of the relation between the user and the contact',
`protocol` char(4) NOT NULL DEFAULT '' COMMENT 'Protocol of the contact',
`subhub` boolean NOT NULL DEFAULT '0' COMMENT '',
`hub-verify` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`rating` tinyint NOT NULL DEFAULT 0 COMMENT 'Automatically detected feed poll frequency',
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Feed poll priority',
`attag` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`hidden` boolean NOT NULL DEFAULT '0' COMMENT '',
`pending` boolean NOT NULL DEFAULT '1' COMMENT 'Contact request is pending',
`deleted` boolean NOT NULL DEFAULT '0' COMMENT 'Contact has been deleted',
`info` mediumtext COMMENT '',
`notify_new_posts` boolean NOT NULL DEFAULT '0' COMMENT '', `notify_new_posts` boolean NOT NULL DEFAULT '0' COMMENT '',
`fetch_further_information` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `fetch_further_information` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`ffi_keyword_denylist` text COMMENT '', `ffi_keyword_denylist` text COMMENT '',
`photo` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo of the contact',
`thumb` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (thumb size)',
`micro` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (micro size)',
`name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`request` varchar(255) COMMENT '',
`confirm` varchar(255) COMMENT '',
`poco` varchar(255) COMMENT '',
`writable` boolean NOT NULL DEFAULT '0' COMMENT '',
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum. Deprecated, use \'contact-type\' = \'community\' and \'manually-approve\' = false instead',
`prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group. Deprecated, use \'contact-type\' = \'community\' and \'manually-approve\' = true instead',
`bdyear` varchar(4) NOT NULL DEFAULT '' COMMENT '',
`site-pubkey` text COMMENT 'Deprecated',
`gender` varchar(32) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`duplex` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`aes_allow` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`ret-aes` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`usehub` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`closeness` tinyint unsigned NOT NULL DEFAULT 99 COMMENT 'Deprecated',
`profile-id` int unsigned COMMENT 'Deprecated',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid_name` (`uid`,`name`(190)), INDEX `uid_name` (`uid`,`name`(190)),
INDEX `self_uid` (`self`,`uid`), INDEX `self_uid` (`self`,`uid`),
@ -1512,13 +1512,28 @@ CREATE TABLE IF NOT EXISTS `userd` (
CREATE TABLE IF NOT EXISTS `user-contact` ( CREATE TABLE IF NOT EXISTS `user-contact` (
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the linked public contact', `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the linked public contact',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url',
`blocked` boolean COMMENT 'Contact is completely blocked for this user', `blocked` boolean COMMENT 'Contact is completely blocked for this user',
`ignored` boolean COMMENT 'Posts from this contact are ignored', `ignored` boolean COMMENT 'Posts from this contact are ignored',
`collapsed` boolean COMMENT 'Posts from this contact are collapsed', `collapsed` boolean COMMENT 'Posts from this contact are collapsed',
`pending` boolean COMMENT '',
`rel` tinyint unsigned COMMENT 'The kind of the relation between the user and the contact',
`info` mediumtext COMMENT '',
`notify_new_posts` boolean COMMENT '',
`remote_self` boolean COMMENT '',
`fetch_further_information` tinyint unsigned COMMENT '',
`ffi_keyword_denylist` text COMMENT '',
`subhub` boolean COMMENT '',
`hub-verify` varchar(255) COMMENT '',
`protocol` char(4) COMMENT 'Protocol of the contact',
`rating` tinyint COMMENT 'Automatically detected feed poll frequency',
`priority` tinyint unsigned COMMENT 'Feed poll priority',
PRIMARY KEY(`uid`,`cid`), PRIMARY KEY(`uid`,`cid`),
INDEX `cid` (`cid`), INDEX `cid` (`cid`),
UNIQUE INDEX `uri-id_uid` (`uri-id`,`uid`),
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE, FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data';
-- --
@ -2225,7 +2240,6 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`location` AS `location`, `contact`.`location` AS `location`,
`contact`.`about` AS `about`, `contact`.`about` AS `about`,
`contact`.`keywords` AS `keywords`, `contact`.`keywords` AS `keywords`,
`contact`.`gender` AS `gender`,
`contact`.`xmpp` AS `xmpp`, `contact`.`xmpp` AS `xmpp`,
`contact`.`matrix` AS `matrix`, `contact`.`matrix` AS `matrix`,
`contact`.`attag` AS `attag`, `contact`.`attag` AS `attag`,
@ -2247,7 +2261,6 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`poll` AS `poll`, `contact`.`poll` AS `poll`,
`contact`.`confirm` AS `confirm`, `contact`.`confirm` AS `confirm`,
`contact`.`poco` AS `poco`, `contact`.`poco` AS `poco`,
`contact`.`usehub` AS `usehub`,
`contact`.`subhub` AS `subhub`, `contact`.`subhub` AS `subhub`,
`contact`.`hub-verify` AS `hub-verify`, `contact`.`hub-verify` AS `hub-verify`,
`contact`.`last-update` AS `last-update`, `contact`.`last-update` AS `last-update`,
@ -2276,9 +2289,7 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`sensitive` AS `sensitive`, `contact`.`sensitive` AS `sensitive`,
`contact`.`baseurl` AS `baseurl`, `contact`.`baseurl` AS `baseurl`,
`contact`.`reason` AS `reason`, `contact`.`reason` AS `reason`,
`contact`.`closeness` AS `closeness`,
`contact`.`info` AS `info`, `contact`.`info` AS `info`,
`contact`.`profile-id` AS `profile-id`,
`contact`.`bdyear` AS `bdyear`, `contact`.`bdyear` AS `bdyear`,
`contact`.`bd` AS `bd`, `contact`.`bd` AS `bd`,
`contact`.`notify_new_posts` AS `notify_new_posts`, `contact`.`notify_new_posts` AS `notify_new_posts`,
@ -2452,7 +2463,7 @@ CREATE VIEW `account-user-view` AS SELECT
`contact`.`network` AS `network`, `contact`.`network` AS `network`,
`ucontact`.`protocol` AS `protocol`, `ucontact`.`protocol` AS `protocol`,
`contact`.`location` AS `location`, `contact`.`location` AS `location`,
`contact`.`attag` AS `attag`, `ucontact`.`attag` AS `attag`,
`contact`.`pubkey` AS `pubkey`, `contact`.`pubkey` AS `pubkey`,
`contact`.`prvkey` AS `prvkey`, `contact`.`prvkey` AS `prvkey`,
`contact`.`subscribe` AS `subscribe`, `contact`.`subscribe` AS `subscribe`,
@ -2468,7 +2479,7 @@ CREATE VIEW `account-user-view` AS SELECT
`contact`.`sensitive` AS `sensitive`, `contact`.`sensitive` AS `sensitive`,
`contact`.`baseurl` AS `baseurl`, `contact`.`baseurl` AS `baseurl`,
`contact`.`gsid` AS `gsid`, `contact`.`gsid` AS `gsid`,
`contact`.`info` AS `info`, `ucontact`.`info` AS `info`,
`contact`.`bdyear` AS `bdyear`, `contact`.`bdyear` AS `bdyear`,
`contact`.`bd` AS `bd`, `contact`.`bd` AS `bd`,
`contact`.`poco` AS `poco`, `contact`.`poco` AS `poco`,

View file

@ -494,7 +494,8 @@ Called when unfollowing a remote contact on a non-native network (like Twitter)
Hook data: Hook data:
- **contact** (input): the remote contact (uid = local unfollowing user id) array. - **contact** (input): the remote contact (uid = local unfollowing user id) array.
- **dissolve** (input): whether to stop sharing with the remote contact as well. - **two_way** (input): wether to stop sharing with the remote contact as well.
- **result** (output): wether the unfollowing is successful or not.
## Complete list of hook callbacks ## Complete list of hook callbacks

View file

@ -7,34 +7,21 @@ Fields
------ ------
| Field | Description | Type | Null | Key | Default | Extra | | Field | Description | Type | Null | Key | Default | Extra |
| ------------------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- | | ------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------ | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | | id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| uid | Owner User id | mediumint unsigned | NO | | 0 | | | uid | Owner User id | mediumint unsigned | NO | | 0 | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | | | created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | Date of last contact update | datetime | YES | | 0001-01-01 00:00:00 | | | updated | Date of last contact update | datetime | YES | | 0001-01-01 00:00:00 | |
| self | 1 if the contact is the user him/her self | boolean | NO | | 0 | |
| remote_self | | boolean | NO | | 0 | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | NO | | 0 | |
| duplex | Deprecated | boolean | NO | | 0 | |
| network | Network of the contact | char(4) | NO | | | | | network | Network of the contact | char(4) | NO | | | |
| protocol | Protocol of the contact | char(4) | NO | | | |
| name | Name that this contact is known by | varchar(255) | NO | | | | | name | Name that this contact is known by | varchar(255) | NO | | | |
| nick | Nick- and user name of the contact | varchar(255) | NO | | | | | nick | Nick- and user name of the contact | varchar(255) | NO | | | |
| location | | varchar(255) | YES | | | | | location | | varchar(255) | YES | | | |
| about | | text | YES | | NULL | | | about | | text | YES | | NULL | |
| keywords | public keywords (interests) of the contact | text | YES | | NULL | | | keywords | public keywords (interests) of the contact | text | YES | | NULL | |
| gender | Deprecated | varchar(32) | NO | | | |
| xmpp | XMPP address | varchar(255) | NO | | | | | xmpp | XMPP address | varchar(255) | NO | | | |
| matrix | Matrix address | varchar(255) | NO | | | | | matrix | Matrix address | varchar(255) | NO | | | |
| attag | | varchar(255) | NO | | | |
| avatar | | varchar(255) | NO | | | | | avatar | | varchar(255) | NO | | | |
| photo | Link to the profile photo of the contact | varchar(255) | YES | | | |
| thumb | Link to the profile photo (thumb size) | varchar(255) | YES | | | |
| micro | Link to the profile photo (micro size) | varchar(255) | YES | | | |
| header | Header picture | varchar(255) | YES | | NULL | | | header | Header picture | varchar(255) | YES | | NULL | |
| site-pubkey | Deprecated | text | YES | | NULL | |
| issued-id | Deprecated | varchar(255) | NO | | | |
| dfrn-id | Deprecated | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | | | url | | varchar(255) | NO | | | |
| nurl | | varchar(255) | NO | | | | | nurl | | varchar(255) | NO | | | |
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | | | uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
@ -43,54 +30,67 @@ Fields
| pubkey | RSA public key 4096 bit | text | YES | | NULL | | | pubkey | RSA public key 4096 bit | text | YES | | NULL | |
| prvkey | RSA private key 4096 bit | text | YES | | NULL | | | prvkey | RSA private key 4096 bit | text | YES | | NULL | |
| batch | | varchar(255) | NO | | | | | batch | | varchar(255) | NO | | | |
| request | | varchar(255) | YES | | NULL | |
| notify | | varchar(255) | YES | | NULL | | | notify | | varchar(255) | YES | | NULL | |
| poll | | varchar(255) | YES | | NULL | | | poll | | varchar(255) | YES | | NULL | |
| confirm | | varchar(255) | YES | | NULL | |
| subscribe | | varchar(255) | YES | | NULL | | | subscribe | | varchar(255) | YES | | NULL | |
| poco | | varchar(255) | YES | | NULL | |
| aes_allow | Deprecated | boolean | NO | | 0 | |
| ret-aes | Deprecated | boolean | NO | | 0 | |
| usehub | | boolean | NO | | 0 | |
| subhub | | boolean | NO | | 0 | |
| hub-verify | | varchar(255) | NO | | | |
| last-update | Date of the last try to update the contact info | datetime | NO | | 0001-01-01 00:00:00 | | | last-update | Date of the last try to update the contact info | datetime | NO | | 0001-01-01 00:00:00 | |
| success_update | Date of the last successful contact update | datetime | NO | | 0001-01-01 00:00:00 | | | success_update | Date of the last successful contact update | datetime | NO | | 0001-01-01 00:00:00 | |
| failure_update | Date of the last failed update | datetime | NO | | 0001-01-01 00:00:00 | | | failure_update | Date of the last failed update | datetime | NO | | 0001-01-01 00:00:00 | |
| failed | Connection failed | boolean | YES | | NULL | | | failed | Connection failed | boolean | YES | | NULL | |
| name-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| uri-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| avatar-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| term-date | | datetime | NO | | 0001-01-01 00:00:00 | | | term-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| last-item | date of the last post | datetime | NO | | 0001-01-01 00:00:00 | | | last-item | date of the last post | datetime | NO | | 0001-01-01 00:00:00 | |
| last-discovery | date of the last follower discovery | datetime | NO | | 0001-01-01 00:00:00 | | | last-discovery | date of the last follower discovery | datetime | NO | | 0001-01-01 00:00:00 | |
| priority | | tinyint unsigned | NO | | 0 | |
| blocked | Node-wide block status | boolean | NO | | 1 | | | blocked | Node-wide block status | boolean | NO | | 1 | |
| block_reason | Node-wide block reason | text | YES | | NULL | | | block_reason | Node-wide block reason | text | YES | | NULL | |
| readonly | posts of the contact are readonly | boolean | NO | | 0 | | | readonly | posts of the contact are readonly | boolean | NO | | 0 | |
| writable | | boolean | NO | | 0 | | | contact-type | Person, organisation, news, community, relay | tinyint | NO | | 0 | |
| forum | contact is a forum | boolean | NO | | 0 | | | manually-approve | Contact requests have to be approved manually | boolean | YES | | NULL | |
| prv | contact is a private group | boolean | NO | | 0 | |
| contact-type | | tinyint | NO | | 0 | |
| manually-approve | | boolean | YES | | NULL | |
| hidden | | boolean | NO | | 0 | |
| archive | | boolean | NO | | 0 | | | archive | | boolean | NO | | 0 | |
| pending | | boolean | NO | | 1 | |
| deleted | Contact has been deleted | boolean | NO | | 0 | |
| rating | | tinyint | NO | | 0 | |
| unsearchable | Contact prefers to not be searchable | boolean | NO | | 0 | | | unsearchable | Contact prefers to not be searchable | boolean | NO | | 0 | |
| sensitive | Contact posts sensitive content | boolean | NO | | 0 | | | sensitive | Contact posts sensitive content | boolean | NO | | 0 | |
| baseurl | baseurl of the contact | varchar(255) | YES | | | | | baseurl | baseurl of the contact | varchar(255) | YES | | | |
| gsid | Global Server ID | int unsigned | YES | | NULL | | | gsid | Global Server ID | int unsigned | YES | | NULL | |
| reason | | text | YES | | NULL | |
| closeness | | tinyint unsigned | NO | | 99 | |
| info | | mediumtext | YES | | NULL | |
| profile-id | Deprecated | int unsigned | YES | | NULL | |
| bdyear | | varchar(4) | NO | | | |
| bd | | date | NO | | 0001-01-01 | | | bd | | date | NO | | 0001-01-01 | |
| reason | | text | YES | | NULL | |
| self | 1 if the contact is the user him/her self | boolean | NO | | 0 | |
| remote_self | | boolean | NO | | 0 | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | NO | | 0 | |
| protocol | Protocol of the contact | char(4) | NO | | | |
| subhub | | boolean | NO | | 0 | |
| hub-verify | | varchar(255) | NO | | | |
| rating | Automatically detected feed poll frequency | tinyint | NO | | 0 | |
| priority | Feed poll priority | tinyint unsigned | NO | | 0 | |
| attag | | varchar(255) | NO | | | |
| hidden | | boolean | NO | | 0 | |
| pending | Contact request is pending | boolean | NO | | 1 | |
| deleted | Contact has been deleted | boolean | NO | | 0 | |
| info | | mediumtext | YES | | NULL | |
| notify_new_posts | | boolean | NO | | 0 | | | notify_new_posts | | boolean | NO | | 0 | |
| fetch_further_information | | tinyint unsigned | NO | | 0 | | | fetch_further_information | | tinyint unsigned | NO | | 0 | |
| ffi_keyword_denylist | | text | YES | | NULL | | | ffi_keyword_denylist | | text | YES | | NULL | |
| photo | Link to the profile photo of the contact | varchar(255) | YES | | | |
| thumb | Link to the profile photo (thumb size) | varchar(255) | YES | | | |
| micro | Link to the profile photo (micro size) | varchar(255) | YES | | | |
| name-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| uri-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| avatar-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| request | | varchar(255) | YES | | NULL | |
| confirm | | varchar(255) | YES | | NULL | |
| poco | | varchar(255) | YES | | NULL | |
| writable | | boolean | NO | | 0 | |
| forum | contact is a forum. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = false instead | boolean | NO | | 0 | |
| prv | contact is a private group. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = true instead | boolean | NO | | 0 | |
| bdyear | | varchar(4) | NO | | | |
| site-pubkey | Deprecated | text | YES | | NULL | |
| gender | Deprecated | varchar(32) | NO | | | |
| duplex | Deprecated | boolean | NO | | 0 | |
| issued-id | Deprecated | varchar(255) | NO | | | |
| dfrn-id | Deprecated | varchar(255) | NO | | | |
| aes_allow | Deprecated | boolean | NO | | 0 | |
| ret-aes | Deprecated | boolean | NO | | 0 | |
| usehub | Deprecated | boolean | NO | | 0 | |
| closeness | Deprecated | tinyint unsigned | NO | | 99 | |
| profile-id | Deprecated | int unsigned | YES | | NULL | |
Indexes Indexes
------------ ------------

View file

@ -7,20 +7,34 @@ Fields
------ ------
| Field | Description | Type | Null | Key | Default | Extra | | Field | Description | Type | Null | Key | Default | Extra |
| --------- | ------------------------------------------- | ------------------ | ---- | --- | ------- | ----- | | ------------------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------- | ----- |
| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | | | cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | |
| uid | User id | mediumint unsigned | NO | PRI | 0 | | | uid | User id | mediumint unsigned | NO | PRI | 0 | |
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | | | blocked | Contact is completely blocked for this user | boolean | YES | | NULL | |
| ignored | Posts from this contact are ignored | boolean | YES | | NULL | | | ignored | Posts from this contact are ignored | boolean | YES | | NULL | |
| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | | | collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | |
| pending | | boolean | YES | | NULL | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | YES | | NULL | |
| info | | mediumtext | YES | | NULL | |
| notify_new_posts | | boolean | YES | | NULL | |
| remote_self | | boolean | YES | | NULL | |
| fetch_further_information | | tinyint unsigned | YES | | NULL | |
| ffi_keyword_denylist | | text | YES | | NULL | |
| subhub | | boolean | YES | | NULL | |
| hub-verify | | varchar(255) | YES | | NULL | |
| protocol | Protocol of the contact | char(4) | YES | | NULL | |
| rating | Automatically detected feed poll frequency | tinyint | YES | | NULL | |
| priority | Feed poll priority | tinyint unsigned | YES | | NULL | |
Indexes Indexes
------------ ------------
| Name | Fields | | Name | Fields |
| ------- | -------- | | ---------- | ------------------- |
| PRIMARY | uid, cid | | PRIMARY | uid, cid |
| cid | cid | | cid | cid |
| uri-id_uid | UNIQUE, uri-id, uid |
Foreign Keys Foreign Keys
------------ ------------
@ -29,5 +43,6 @@ Foreign Keys
|-------|--------------|--------------| |-------|--------------|--------------|
| cid | [contact](help/database/db_contact) | id | | cid | [contact](help/database/db_contact) | id |
| uid | [user](help/database/db_user) | uid | | uid | [user](help/database/db_user) | uid |
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database) Return to [database documentation](help/database)

View file

@ -3778,11 +3778,11 @@ api_register_func('api/direct_messages/destroy', 'api_direct_messages_destroy',
* *
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json' * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string|array * @return string|array
* @throws BadRequestException * @throws HTTPException\BadRequestException
* @throws ForbiddenException * @throws HTTPException\ExpectationFailedException
* @throws ImagickException * @throws HTTPException\ForbiddenException
* @throws InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws NotFoundException * @throws HTTPException\NotFoundException
* @see https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/post-friendships-destroy.html * @see https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/post-friendships-destroy.html
*/ */
function api_friendships_destroy($type) function api_friendships_destroy($type)
@ -3790,25 +3790,31 @@ function api_friendships_destroy($type)
$uid = api_user(); $uid = api_user();
if ($uid === false) { if ($uid === false) {
throw new ForbiddenException(); throw new HTTPException\ForbiddenException();
}
$owner = User::getOwnerDataById($uid);
if (!$owner) {
Logger::notice(API_LOG_PREFIX . 'No owner {uid} found', ['module' => 'api', 'action' => 'friendships_destroy', 'uid' => $uid]);
throw new HTTPException\NotFoundException('Error Processing Request');
} }
$contact_id = $_REQUEST['user_id'] ?? 0; $contact_id = $_REQUEST['user_id'] ?? 0;
if (empty($contact_id)) { if (empty($contact_id)) {
Logger::notice(API_LOG_PREFIX . 'No user_id specified', ['module' => 'api', 'action' => 'friendships_destroy']); Logger::notice(API_LOG_PREFIX . 'No user_id specified', ['module' => 'api', 'action' => 'friendships_destroy']);
throw new BadRequestException("no user_id specified"); throw new HTTPException\BadRequestException('no user_id specified');
} }
// Get Contact by given id // Get Contact by given id
$contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => 0, 'self' => false]); $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => 0, 'self' => false]);
if(!DBA::isResult($contact)) { if(!DBA::isResult($contact)) {
Logger::notice(API_LOG_PREFIX . 'No contact found for ID {contact}', ['module' => 'api', 'action' => 'friendships_destroy', 'contact' => $contact_id]); Logger::notice(API_LOG_PREFIX . 'No public contact found for ID {contact}', ['module' => 'api', 'action' => 'friendships_destroy', 'contact' => $contact_id]);
throw new NotFoundException("no contact found to given ID"); throw new HTTPException\NotFoundException('no contact found to given ID');
} }
$url = $contact["url"]; $url = $contact['url'];
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)", $condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url), $uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
@ -3817,40 +3823,35 @@ function api_friendships_destroy($type)
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
Logger::notice(API_LOG_PREFIX . 'Not following contact', ['module' => 'api', 'action' => 'friendships_destroy']); Logger::notice(API_LOG_PREFIX . 'Not following contact', ['module' => 'api', 'action' => 'friendships_destroy']);
throw new NotFoundException("Not following Contact"); throw new HTTPException\NotFoundException('Not following Contact');
}
if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
Logger::notice(API_LOG_PREFIX . 'Not supported for {network}', ['module' => 'api', 'action' => 'friendships_destroy', 'network' => $contact['network']]);
throw new ExpectationFailedException("Not supported");
} }
$dissolve = ($contact['rel'] == Contact::SHARING); $dissolve = ($contact['rel'] == Contact::SHARING);
$owner = User::getOwnerDataById($uid); try {
if ($owner) { $result = Contact::terminateFriendship($owner, $contact, $dissolve);
Contact::terminateFriendship($owner, $contact, $dissolve);
} if ($result === null) {
else { Logger::notice(API_LOG_PREFIX . 'Not supported for {network}', ['module' => 'api', 'action' => 'friendships_destroy', 'network' => $contact['network']]);
Logger::notice(API_LOG_PREFIX . 'No owner {uid} found', ['module' => 'api', 'action' => 'friendships_destroy', 'uid' => $uid]); throw new HTTPException\ExpectationFailedException('Unfollowing is currently not supported by this contact\'s network.');
throw new NotFoundException("Error Processing Request");
} }
// Sharing-only contacts get deleted as there no relationship any more if ($result === false) {
if ($dissolve) { throw new HTTPException\ServiceUnavailableException('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
Contact::remove($contact['id']); }
} else { } catch (Exception $e) {
DBA::update('contact', ['rel' => Contact::FOLLOWER], ['id' => $contact['id']]); Logger::error(API_LOG_PREFIX . $e->getMessage(), ['owner' => $owner, 'contact' => $contact, 'dissolve' => $dissolve]);
throw new HTTPException\InternalServerErrorException('Unable to unfollow this contact, please contact your administrator');
} }
// "uid" and "self" are only needed for some internal stuff, so remove it from here // "uid" and "self" are only needed for some internal stuff, so remove it from here
unset($contact["uid"]); unset($contact['uid']);
unset($contact["self"]); unset($contact['self']);
// Set screen_name since Twidere requests it // Set screen_name since Twidere requests it
$contact["screen_name"] = $contact["nick"]; $contact['screen_name'] = $contact['nick'];
return api_format_data("friendships-destroy", $type, ['user' => $contact]); return api_format_data('friendships-destroy', $type, ['user' => $contact]);
} }
api_register_func('api/friendships/destroy', 'api_friendships_destroy', true, API_METHOD_POST); api_register_func('api/friendships/destroy', 'api_friendships_destroy', true, API_METHOD_POST);
@ -4496,14 +4497,14 @@ function api_account_update_profile($type)
if (!empty($_POST['name'])) { if (!empty($_POST['name'])) {
DBA::update('profile', ['name' => $_POST['name']], ['uid' => $local_user]); DBA::update('profile', ['name' => $_POST['name']], ['uid' => $local_user]);
DBA::update('user', ['username' => $_POST['name']], ['uid' => $local_user]); DBA::update('user', ['username' => $_POST['name']], ['uid' => $local_user]);
DBA::update('contact', ['name' => $_POST['name']], ['uid' => $local_user, 'self' => 1]); Contact::update(['name' => $_POST['name']], ['uid' => $local_user, 'self' => 1]);
DBA::update('contact', ['name' => $_POST['name']], ['id' => $api_user['id']]); Contact::update(['name' => $_POST['name']], ['id' => $api_user['id']]);
} }
if (isset($_POST['description'])) { if (isset($_POST['description'])) {
DBA::update('profile', ['about' => $_POST['description']], ['uid' => $local_user]); DBA::update('profile', ['about' => $_POST['description']], ['uid' => $local_user]);
DBA::update('contact', ['about' => $_POST['description']], ['uid' => $local_user, 'self' => 1]); Contact::update(['about' => $_POST['description']], ['uid' => $local_user, 'self' => 1]);
DBA::update('contact', ['about' => $_POST['description']], ['id' => $api_user['id']]); Contact::update(['about' => $_POST['description']], ['id' => $api_user['id']]);
} }
Profile::publishUpdate($local_user); Profile::publishUpdate($local_user);

View file

@ -96,7 +96,7 @@ function pubsub_init(App $a)
} }
if (!empty($hub_mode)) { if (!empty($hub_mode)) {
DBA::update('contact', ['subhub' => $subscribe], ['id' => $contact['id']]); Contact::update(['subhub' => $subscribe], ['id' => $contact['id']]);
Logger::log($hub_mode . ' success for contact ' . $contact_id . '.'); Logger::log($hub_mode . ' success for contact ' . $contact_id . '.');
} }
hub_return(true, $hub_challenge); hub_return(true, $hub_challenge);

View file

@ -120,6 +120,12 @@ function unfollow_process(string $url)
$uid = local_user(); $uid = local_user();
$owner = User::getOwnerDataById($uid);
if (!$owner) {
\Friendica\Module\Security\Logout::init();
// NOTREACHED
}
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)", $condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
$uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url), $uid, Contact::SHARING, Contact::FRIEND, Strings::normaliseLink($url),
Strings::normaliseLink($url), $url]; Strings::normaliseLink($url), $url];
@ -131,27 +137,30 @@ function unfollow_process(string $url)
// NOTREACHED // NOTREACHED
} }
if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
notice(DI::l10n()->t('Unfollowing is currently not supported by your network.'));
DI::baseUrl()->redirect($base_return_path . '/' . $contact['id']);
// NOTREACHED
}
$dissolve = ($contact['rel'] == Contact::SHARING); $dissolve = ($contact['rel'] == Contact::SHARING);
$owner = User::getOwnerDataById($uid); $notice_message = '';
if ($owner) {
Contact::terminateFriendship($owner, $contact, $dissolve);
}
// Sharing-only contacts get deleted as there no relationship anymore
if ($dissolve) {
Contact::remove($contact['id']);
$return_path = $base_return_path;
} else {
DBA::update('contact', ['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
$return_path = $base_return_path . '/' . $contact['id']; $return_path = $base_return_path . '/' . $contact['id'];
try {
$result = Contact::terminateFriendship($owner, $contact, $dissolve);
if ($result === null) {
$notice_message = DI::l10n()->t('Unfollowing is currently not supported by this contact\'s network.');
} }
if ($result === false) {
$notice_message = DI::l10n()->t('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
}
if ($result === true) {
$notice_message = DI::l10n()->t('Contact was successfully unfollowed');
}
} catch (Exception $e) {
DI::logger()->error($e->getMessage(), ['owner' => $owner, 'contact' => $contact, 'dissolve' => $dissolve]);
$notice_message = DI::l10n()->t('Unable to unfollow this contact, please contact your administrator');
}
notice($notice_message);
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
} }

View file

@ -24,6 +24,7 @@ namespace Friendica\Console;
use Friendica\App; use Friendica\App;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use RuntimeException; use RuntimeException;
@ -104,7 +105,7 @@ HELP;
if (!$this->dba->exists('contact', ['nurl' => $nurl, 'archive' => false])) { if (!$this->dba->exists('contact', ['nurl' => $nurl, 'archive' => false])) {
throw new RuntimeException(DI::l10n()->t('Could not find any unarchived contact entry for this URL (%s)', $nurl)); throw new RuntimeException(DI::l10n()->t('Could not find any unarchived contact entry for this URL (%s)', $nurl));
} }
if ($this->dba->update('contact', ['archive' => true], ['nurl' => $nurl])) { if (Contact::update(['archive' => true], ['nurl' => $nurl])) {
$this->out($this->l10n->t('The contact entries have been archived')); $this->out($this->l10n->t('The contact entries have been archived'));
} else { } else {
throw new RuntimeException('The contact archival failed.'); throw new RuntimeException('The contact archival failed.');

View file

@ -23,6 +23,7 @@ namespace Friendica\Console;
use Console_Table; use Console_Table;
use Friendica\App; use Friendica\App;
use Friendica\DI;
use Friendica\Model\Contact as ContactModel; use Friendica\Model\Contact as ContactModel;
use Friendica\Model\User as UserModel; use Friendica\Model\User as UserModel;
use Friendica\Network\Probe; use Friendica\Network\Probe;
@ -177,11 +178,12 @@ HELP;
} }
/** /**
* Sends an unfriend message. Does not remove the contact * Sends an unfriend message.
* *
* @return bool True, if the command was successful * @return bool True, if the command was successful
* @throws \Exception
*/ */
private function terminateContact() private function terminateContact(): bool
{ {
$cid = $this->getArgument(1); $cid = $this->getArgument(1);
if (empty($cid)) { if (empty($cid)) {
@ -199,7 +201,23 @@ HELP;
$user = UserModel::getById($contact['uid']); $user = UserModel::getById($contact['uid']);
try {
$result = ContactModel::terminateFriendship($user, $contact); $result = ContactModel::terminateFriendship($user, $contact);
if ($result === null) {
throw new RuntimeException('Unfollowing is currently not supported by this contact\'s network.');
}
if ($result === false) {
throw new RuntimeException('Unable to unfollow this contact, please retry in a few minutes or check the logs.');
}
$this->out('Contact was successfully unfollowed');
return true;
} catch (\Exception $e) {
DI::logger()->error($e->getMessage(), ['owner' => $user, 'contact' => $contact]);
throw new RuntimeException('Unable to unfollow this contact, please check the log');
}
} }
/** /**

View file

@ -98,7 +98,7 @@ HELP;
$contact_id = Contact::getIdForURL($this->getArgument(0)); $contact_id = Contact::getIdForURL($this->getArgument(0));
if ($contact_id) { if ($contact_id) {
$this->dba->update('contact', ['hidden' => true], ['id' => $contact_id]); Contact::update(['hidden' => true], ['id' => $contact_id]);
$this->out('The account has been successfully silenced from the global community page.'); $this->out('The account has been successfully silenced from the global community page.');
} else { } else {
throw new RuntimeException('Could not find any public contact entry for this URL (' . $this->getArgument(0) . ')'); throw new RuntimeException('Could not find any public contact entry for this URL (' . $this->getArgument(0) . ')');

View file

@ -22,6 +22,12 @@
namespace Friendica\Core; namespace Friendica\Core;
use Friendica\DI; use Friendica\DI;
use Friendica\Network\HTTPException;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Protocol\Salmon;
/** /**
* Manage compatibility with federated networks * Manage compatibility with federated networks
@ -157,4 +163,63 @@ class Protocol
{ {
return $display_name . ' (' . self::getAddrFromProfileUrl($profile_url) . ')'; return $display_name . ' (' . self::getAddrFromProfileUrl($profile_url) . ')';
} }
/**
* Sends an unfriend message. Does not remove the contact
*
* @param array $user User unfriending
* @param array $contact Contact unfriended
* @param boolean $two_way Revoke eventual inbound follow as well
* @return bool|null true if successful, false if not, null if no action was performed
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function terminateFriendship(array $user, array $contact, bool $two_way = false): bool
{
if (empty($contact['network'])) {
throw new \InvalidArgumentException('Missing network key in contact array');
}
$protocol = $contact['network'];
if (($protocol == Protocol::DFRN) && !empty($contact['protocol'])) {
$protocol = $contact['protocol'];
}
if (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) {
// create an unfollow slap
$item = [];
$item['verb'] = Activity::O_UNFOLLOW;
$item['gravity'] = GRAVITY_ACTIVITY;
$item['follow'] = $contact['url'];
$item['body'] = '';
$item['title'] = '';
$item['guid'] = '';
$item['uri-id'] = 0;
$slap = OStatus::salmon($item, $user);
if (empty($contact['notify'])) {
throw new \InvalidArgumentException('Missing expected "notify" key in OStatus/DFRN contact');
}
return Salmon::slapper($user, $contact['notify'], $slap) === 0;
} elseif ($protocol == Protocol::DIASPORA) {
return Diaspora::sendUnshare($user, $contact) > 0;
} elseif ($protocol == Protocol::ACTIVITYPUB) {
if ($two_way) {
ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $user['uid']);
}
return ActivityPub\Transmitter::sendContactUndo($contact['url'], $contact['id'], $user['uid']);
}
// Catch-all addon hook
$hook_data = [
'contact' => $contact,
'two_way' => $two_way,
'result' => null
];
Hook::callAll('unfollow', $hook_data);
return $hook_data['result'];
}
} }

View file

@ -133,6 +133,7 @@ class Contact
const FOLLOWER = 1; const FOLLOWER = 1;
const SHARING = 2; const SHARING = 2;
const FRIEND = 3; const FRIEND = 3;
const SELF = 4;
/** /**
* @} * @}
*/ */
@ -175,7 +176,7 @@ class Contact
* @param array $fields field array * @param array $fields field array
* @param int $duplicate_mode Do an update on a duplicate entry * @param int $duplicate_mode Do an update on a duplicate entry
* *
* @return boolean was the insert successful? * @return int id of the created contact
* @throws \Exception * @throws \Exception
*/ */
public static function insert(array $fields, int $duplicate_mode = Database::INSERT_DEFAULT) public static function insert(array $fields, int $duplicate_mode = Database::INSERT_DEFAULT)
@ -190,15 +191,40 @@ class Contact
$fields['created'] = DateTimeFormat::utcNow(); $fields['created'] = DateTimeFormat::utcNow();
} }
$ret = DBA::insert('contact', $fields, $duplicate_mode); DBA::insert('contact', $fields, $duplicate_mode);
$contact = DBA::selectFirst('contact', ['nurl', 'uid'], ['id' => DBA::lastInsertId()]); $contact = DBA::selectFirst('contact', [], ['id' => DBA::lastInsertId()]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
// Shouldn't happen // Shouldn't happen
return $ret; Logger::warning('Created contact could not be found', ['fields' => $fields]);
return 0;
} }
Contact\User::insertForContactArray($contact);
// Search for duplicated contacts and get rid of them // Search for duplicated contacts and get rid of them
if (!$contact['self']) {
self::removeDuplicates($contact['nurl'], $contact['uid']); self::removeDuplicates($contact['nurl'], $contact['uid']);
}
return $contact['id'];
}
/**
* Updates rows in the contact table
*
* @param array $fields contains the fields that are updated
* @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)
*
* @return boolean was the update successfull?
* @throws \Exception
*/
public static function update(array $fields, array $condition, $old_fields = [])
{
$ret = DBA::update('contact', $fields, $condition, $old_fields);
// Apply changes to the "user-contact" table on dedicated fields
Contact\User::updateByContactUpdate($fields, $condition);
return $ret; return $ret;
} }
@ -650,7 +676,7 @@ class Contact
// Only create the entry if it doesn't exist yet // Only create the entry if it doesn't exist yet
if (!DBA::exists('contact', ['uid' => $uid, 'self' => true])) { if (!DBA::exists('contact', ['uid' => $uid, 'self' => true])) {
$return = DBA::insert('contact', $contact); $return = (bool)self::insert($contact);
} }
// Create the public contact // Create the public contact
@ -659,7 +685,7 @@ class Contact
$contact['uid'] = 0; $contact['uid'] = 0;
$contact['prvkey'] = null; $contact['prvkey'] = null;
DBA::insert('contact', $contact, Database::INSERT_IGNORE); self::insert($contact, Database::INSERT_IGNORE);
} }
return $return; return $return;
@ -760,12 +786,12 @@ class Contact
$fields['name-date'] = DateTimeFormat::utcNow(); $fields['name-date'] = DateTimeFormat::utcNow();
} }
$fields['updated'] = DateTimeFormat::utcNow(); $fields['updated'] = DateTimeFormat::utcNow();
DBA::update('contact', $fields, ['id' => $self['id']]); self::update($fields, ['id' => $self['id']]);
// Update the public contact as well // Update the public contact as well
$fields['prvkey'] = null; $fields['prvkey'] = null;
$fields['self'] = false; $fields['self'] = false;
DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $self['nurl']]); self::update($fields, ['uid' => 0, 'nurl' => $self['nurl']]);
// Update the profile // Update the profile
$fields = [ $fields = [
@ -783,7 +809,6 @@ class Contact
* Marks a contact for removal * Marks a contact for removal
* *
* @param int $id contact id * @param int $id contact id
* @return null
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function remove($id) public static function remove($id)
@ -795,63 +820,33 @@ class Contact
} }
// Archive the contact // Archive the contact
DBA::update('contact', ['archive' => true, 'network' => Protocol::PHANTOM, 'deleted' => true], ['id' => $id]); self::update(['archive' => true, 'network' => Protocol::PHANTOM, 'deleted' => true], ['id' => $id]);
// Delete it in the background // Delete it in the background
Worker::add(PRIORITY_MEDIUM, 'RemoveContact', $id); Worker::add(PRIORITY_MEDIUM, 'RemoveContact', $id);
} }
/** /**
* Sends an unfriend message. Does not remove the contact * Sends an unfriend message. Removes the contact for two-way unfriending or sharing only protocols (feed an mail)
* *
* @param array $user User unfriending * @param array $user User unfriending
* @param array $contact Contact unfriended * @param array $contact Contact unfriended
* @param boolean $dissolve Remove the contact on the remote side * @param boolean $two_way Revoke eventual inbound follow as well
* @return void * @return bool|null true if successful, false if not, null if no action was performed
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public static function terminateFriendship(array $user, array $contact, $dissolve = false) public static function terminateFriendship(array $user, array $contact, bool $two_way = false): bool
{ {
if (empty($contact['network'])) { $result = Protocol::terminateFriendship($user, $contact, $two_way);
return;
}
$protocol = $contact['network']; if ($two_way || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
if (($protocol == Protocol::DFRN) && !empty($contact['protocol'])) { self::remove($contact['id']);
$protocol = $contact['protocol'];
}
if (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) {
// create an unfollow slap
$item = [];
$item['verb'] = Activity::O_UNFOLLOW;
$item['gravity'] = GRAVITY_ACTIVITY;
$item['follow'] = $contact["url"];
$item['body'] = '';
$item['title'] = '';
$item['guid'] = '';
$item['uri-id'] = 0;
$slap = OStatus::salmon($item, $user);
if (!empty($contact['notify'])) {
Salmon::slapper($user, $contact['notify'], $slap);
}
} elseif ($protocol == Protocol::DIASPORA) {
Diaspora::sendUnshare($user, $contact);
} elseif ($protocol == Protocol::ACTIVITYPUB) {
ActivityPub\Transmitter::sendContactUndo($contact['url'], $contact['id'], $user['uid']);
if ($dissolve) {
ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $user['uid']);
}
} else { } else {
$hook_data = [ self::update(['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
'contact' => $contact,
'dissolve' => $dissolve,
];
Hook::callAll('unfollow', $hook_data);
} }
return $result;
} }
/** /**
@ -887,8 +882,8 @@ class Contact
} }
if ($contact['term-date'] <= DBA::NULL_DATETIME) { if ($contact['term-date'] <= DBA::NULL_DATETIME) {
DBA::update('contact', ['term-date' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); self::update(['term-date' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
DBA::update('contact', ['term-date' => DateTimeFormat::utcNow()], ['`nurl` = ? AND `term-date` <= ? AND NOT `self`', Strings::normaliseLink($contact['url']), DBA::NULL_DATETIME]); self::update(['term-date' => DateTimeFormat::utcNow()], ['`nurl` = ? AND `term-date` <= ? AND NOT `self`', Strings::normaliseLink($contact['url']), DBA::NULL_DATETIME]);
} else { } else {
/* @todo /* @todo
* We really should send a notification to the owner after 2-3 weeks * We really should send a notification to the owner after 2-3 weeks
@ -905,8 +900,8 @@ class Contact
* delete, though if the owner tries to unarchive them we'll start * delete, though if the owner tries to unarchive them we'll start
* the whole process over again. * the whole process over again.
*/ */
DBA::update('contact', ['archive' => true], ['id' => $contact['id']]); self::update(['archive' => true], ['id' => $contact['id']]);
DBA::update('contact', ['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); self::update(['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
} }
} }
} }
@ -927,7 +922,7 @@ class Contact
$fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false];
$condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY]; $condition = ['uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
if (!DBA::exists('contact', array_merge($condition, $fields))) { if (!DBA::exists('contact', array_merge($condition, $fields))) {
DBA::update('contact', $fields, $condition); self::update($fields, $condition);
} }
} }
@ -951,8 +946,8 @@ class Contact
// It's a miracle. Our dead contact has inexplicably come back to life. // It's a miracle. Our dead contact has inexplicably come back to life.
$fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false]; $fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false];
DBA::update('contact', $fields, ['id' => $contact['id']]); self::update($fields, ['id' => $contact['id']]);
DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); self::update($fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
} }
/** /**
@ -1218,8 +1213,7 @@ class Contact
$contact_id = $contact['id']; $contact_id = $contact['id'];
Logger::notice('Contact had been created (shortly) before', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]); Logger::notice('Contact had been created (shortly) before', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]);
} else { } else {
DBA::insert('contact', $fields); $contact_id = self::insert($fields);
$contact_id = DBA::lastInsertId();
if ($contact_id) { if ($contact_id) {
Logger::info('Contact inserted', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]); Logger::info('Contact inserted', ['id' => $contact_id, 'url' => $url, 'uid' => $uid]);
} }
@ -1508,7 +1502,7 @@ class Contact
*/ */
public static function block($cid, $reason = null) public static function block($cid, $reason = null)
{ {
$return = DBA::update('contact', ['blocked' => true, 'block_reason' => $reason], ['id' => $cid]); $return = self::update(['blocked' => true, 'block_reason' => $reason], ['id' => $cid]);
return $return; return $return;
} }
@ -1522,7 +1516,7 @@ class Contact
*/ */
public static function unblock($cid) public static function unblock($cid)
{ {
$return = DBA::update('contact', ['blocked' => false, 'block_reason' => null], ['id' => $cid]); $return = self::update(['blocked' => false, 'block_reason' => null], ['id' => $cid]);
return $return; return $return;
} }
@ -1821,7 +1815,7 @@ class Contact
// Only update the cached photo links of public contacts when they already are cached // Only update the cached photo links of public contacts when they already are cached
if (($uid == 0) && !$force && empty($contact['thumb']) && empty($contact['micro']) && !$create_cache) { if (($uid == 0) && !$force && empty($contact['thumb']) && empty($contact['micro']) && !$create_cache) {
if ($contact['avatar'] != $avatar) { if ($contact['avatar'] != $avatar) {
DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]); self::update(['avatar' => $avatar], ['id' => $cid]);
Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]); Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]);
} }
return; return;
@ -1918,7 +1912,7 @@ class Contact
$cids[] = $cid; $cids[] = $cid;
$uids[] = $uid; $uids[] = $uid;
Logger::info('Updating cached contact avatars', ['cid' => $cids, 'uid' => $uids, 'fields' => $fields]); Logger::info('Updating cached contact avatars', ['cid' => $cids, 'uid' => $uids, 'fields' => $fields]);
DBA::update('contact', $fields, ['id' => $cids]); self::update($fields, ['id' => $cids]);
} }
public static function deleteContactByUrl(string $url) public static function deleteContactByUrl(string $url)
@ -1945,7 +1939,7 @@ class Contact
*/ */
private static function updateContact(int $id, int $uid, string $old_url, string $new_url, array $fields) private static function updateContact(int $id, int $uid, string $old_url, string $new_url, array $fields)
{ {
if (!DBA::update('contact', $fields, ['id' => $id])) { if (!self::update($fields, ['id' => $id])) {
Logger::info('Couldn\'t update contact.', ['id' => $id, 'fields' => $fields]); Logger::info('Couldn\'t update contact.', ['id' => $id, 'fields' => $fields]);
return; return;
} }
@ -1978,7 +1972,7 @@ class Contact
$condition = ['self' => false, 'nurl' => Strings::normaliseLink($old_url)]; $condition = ['self' => false, 'nurl' => Strings::normaliseLink($old_url)];
$condition['network'] = [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB]; $condition['network'] = [Protocol::DFRN, Protocol::DIASPORA, Protocol::ACTIVITYPUB];
DBA::update('contact', $fields, $condition); self::update($fields, $condition);
// We mustn't set the update fields for OStatus contacts since they are updated in OnePoll // We mustn't set the update fields for OStatus contacts since they are updated in OnePoll
$condition['network'] = Protocol::OSTATUS; $condition['network'] = Protocol::OSTATUS;
@ -1994,7 +1988,7 @@ class Contact
return; return;
} }
DBA::update('contact', $fields, $condition); self::update($fields, $condition);
} }
/** /**
@ -2007,7 +2001,7 @@ class Contact
*/ */
public static function removeDuplicates(string $nurl, int $uid) public static function removeDuplicates(string $nurl, int $uid)
{ {
$condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false, 'network' => Protocol::FEDERATED]; $condition = ['nurl' => $nurl, 'uid' => $uid, 'self' => false, 'deleted' => false, 'network' => Protocol::FEDERATED];
$count = DBA::count('contact', $condition); $count = DBA::count('contact', $condition);
if ($count <= 1) { if ($count <= 1) {
return false; return false;
@ -2268,7 +2262,7 @@ class Contact
} }
} }
if (!empty($fields)) { if (!empty($fields)) {
DBA::update('contact', $fields, ['id' => $id, 'self' => false]); self::update($fields, ['id' => $id, 'self' => false]);
Logger::info('Updating local contact', ['id' => $id]); Logger::info('Updating local contact', ['id' => $id]);
} }
} }
@ -2446,7 +2440,7 @@ class Contact
$new_relation = (($contact['rel'] == self::FOLLOWER) ? self::FRIEND : self::SHARING); $new_relation = (($contact['rel'] == self::FOLLOWER) ? self::FRIEND : self::SHARING);
$fields = ['rel' => $new_relation, 'subhub' => $subhub, 'readonly' => false]; $fields = ['rel' => $new_relation, 'subhub' => $subhub, 'readonly' => false];
DBA::update('contact', $fields, ['id' => $contact['id']]); self::update($fields, ['id' => $contact['id']]);
} else { } else {
$new_relation = (in_array($protocol, [Protocol::MAIL]) ? self::FRIEND : self::SHARING); $new_relation = (in_array($protocol, [Protocol::MAIL]) ? self::FRIEND : self::SHARING);
@ -2579,7 +2573,7 @@ class Contact
$fields = ['url' => $contact['url'], 'request' => $contact['request'], $fields = ['url' => $contact['url'], 'request' => $contact['request'],
'notify' => $contact['notify'], 'poll' => $contact['poll'], 'notify' => $contact['notify'], 'poll' => $contact['poll'],
'confirm' => $contact['confirm'], 'poco' => $contact['poco']]; 'confirm' => $contact['confirm'], 'poco' => $contact['poco']];
DBA::update('contact', $fields, ['id' => $contact['id']]); self::update($fields, ['id' => $contact['id']]);
} }
return $contact; return $contact;
@ -2682,7 +2676,7 @@ class Contact
if (($contact['rel'] == self::SHARING) if (($contact['rel'] == self::SHARING)
|| ($sharing && $contact['rel'] == self::FOLLOWER)) { || ($sharing && $contact['rel'] == self::FOLLOWER)) {
DBA::update('contact', ['rel' => self::FRIEND, 'writable' => true, 'pending' => false], self::update(['rel' => self::FRIEND, 'writable' => true, 'pending' => false],
['id' => $contact['id'], 'uid' => $importer['uid']]); ['id' => $contact['id'], 'uid' => $importer['uid']]);
} }
@ -2700,7 +2694,7 @@ class Contact
} }
// create contact record // create contact record
DBA::insert('contact', [ $contact_id = self::insert([
'uid' => $importer['uid'], 'uid' => $importer['uid'],
'created' => DateTimeFormat::utcNow(), 'created' => DateTimeFormat::utcNow(),
'url' => $url, 'url' => $url,
@ -2715,8 +2709,6 @@ class Contact
'writable' => 1, 'writable' => 1,
]); ]);
$contact_id = DBA::lastInsertId();
// Ensure to always have the correct network type, independent from the connection request method // Ensure to always have the correct network type, independent from the connection request method
self::updateFromProbe($contact_id); self::updateFromProbe($contact_id);
@ -2764,7 +2756,7 @@ class Contact
$fields['rel'] = self::FRIEND; $fields['rel'] = self::FRIEND;
} }
DBA::update('contact', $fields, $condition); self::update($fields, $condition);
return true; return true;
} }
@ -2787,7 +2779,7 @@ class Contact
public static function removeSharer($importer, $contact) public static function removeSharer($importer, $contact)
{ {
if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) { if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) {
DBA::update('contact', ['rel' => self::FOLLOWER], ['id' => $contact['id']]); self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
} else { } else {
self::remove($contact['id']); self::remove($contact['id']);
} }

View file

@ -114,7 +114,7 @@ class Relation
} }
if (empty($followers) && empty($followings)) { if (empty($followers) && empty($followings)) {
DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]); Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]); Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]);
return; return;
} }
@ -162,7 +162,7 @@ class Relation
DBA::delete('contact-relation', ['cid' => $target, 'follows' => false, 'last-interaction' => DBA::NULL_DATETIME]); DBA::delete('contact-relation', ['cid' => $target, 'follows' => false, 'last-interaction' => DBA::NULL_DATETIME]);
} }
DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]); Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]);
Logger::info('Contacts discovery finished', ['id' => $target, 'url' => $url, 'follower' => $follower_counter, 'following' => $following_counter]); Logger::info('Contacts discovery finished', ['id' => $target, 'url' => $url, 'follower' => $follower_counter, 'following' => $following_counter]);
return; return;
} }

View file

@ -21,14 +21,115 @@
namespace Friendica\Model\Contact; namespace Friendica\Model\Contact;
use Exception;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Database\Database;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\Database\DBStructure;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\ItemURI;
use PDOException;
/** /**
* This class provides information about user related contacts based on the "user-contact" table. * This class provides information about user related contacts based on the "user-contact" table.
*/ */
class User class User
{ {
/**
* Insert a user-contact for a given contact array
*
* @param array $contact
* @return void
*/
public static function insertForContactArray(array $contact)
{
if (empty($contact['uid'])) {
// We don't create entries for the public user - by now
return false;
}
if (empty($contact['uri-id']) && empty($contact['url'])) {
Logger::info('Missing contact details', ['contact' => $contact, 'callstack' => System::callstack(20)]);
return false;
}
if (empty($contact['uri-id'])) {
$contact['uri-id'] = ItemURI::getIdByURI($contact['url']);
}
$pcontact = Contact::selectFirst(['id'], ['uri-id' => $contact['uri-id'], 'uid' => 0]);
if (!empty($contact['uri-id']) && DBA::isResult($pcontact)) {
$pcid = $pcontact['id'];
} elseif (empty($contact['url']) || !($pcid = Contact::getIdForURL($contact['url'], 0, false))) {
Logger::info('Public contact for user not found', ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid']]);
return false;
}
$fields = self::preparedFields($contact);
$fields['cid'] = $pcid;
$fields['uid'] = $contact['uid'];
$fields['uri-id'] = $contact['uri-id'];
$ret = DBA::insert('user-contact', $fields, Database::INSERT_UPDATE);
Logger::info('Inserted user contact', ['uid' => $contact['uid'], 'cid' => $pcid, 'uri-id' => $contact['uri-id'], 'ret' => $ret]);
return $ret;
}
/**
* Apply changes from contact update data to user-contact table
*
* @param array $fields
* @param array $condition
* @return void
* @throws PDOException
* @throws Exception
*/
public static function updateByContactUpdate(array $fields, array $condition)
{
DBA::transaction();
$update_fields = self::preparedFields($fields);
if (!empty($update_fields)) {
$contacts = DBA::select('contact', ['uri-id', 'uid'], $condition);
while ($row = DBA::fetch($contacts)) {
if (empty($row['uri-id']) || empty($contact['uid'])) {
continue;
}
$ret = DBA::update('user-contact', $update_fields, ['uri-id' => $row['uri-id'], 'uid' => $row['uid']]);
Logger::info('Updated user contact', ['uid' => $row['uid'], 'uri-id' => $row['uri-id'], 'ret' => $ret]);
}
DBA::close($contacts);
}
DBA::commit();
}
/**
* Prepare field data for update/insert
*
* @param array $fields
* @return array prepared fields
*/
private static function preparedFields(array $fields): array
{
unset($fields['uid']);
unset($fields['cid']);
unset($fields['uri-id']);
if (isset($fields['readonly'])) {
$fields['ignored'] = $fields['readonly'];
}
if (!empty($fields['self'])) {
$fields['rel'] = Contact::SELF;
}
return DBStructure::getFieldsForTable('user-contact', $fields);
}
/** /**
* Block contact id for user id * Block contact id for user id
* *

View file

@ -110,7 +110,7 @@ class Introduction extends BaseModel
'hidden' => $hidden ?? $contact['hidden'], 'hidden' => $hidden ?? $contact['hidden'],
'rel' => $newRelation, 'rel' => $newRelation,
]; ];
$this->dba->update('contact', $fields, ['id' => $contact['id']]); Contact::update($fields, ['id' => $contact['id']]);
array_merge($contact, $fields); array_merge($contact, $fields);
@ -159,7 +159,7 @@ class Introduction extends BaseModel
if ($this->dba->exists('contact', $condition)) { if ($this->dba->exists('contact', $condition)) {
Contact::remove($this->{'contact-id'}); Contact::remove($this->{'contact-id'});
} else { } else {
$this->dba->update('contact', ['pending' => false], ['id' => $this->{'contact-id'}]); Contact::update(['pending' => false], ['id' => $this->{'contact-id'}]);
} }
} }

View file

@ -1766,15 +1766,15 @@ class Item
} else { } else {
$condition = ['id' => $arr['contact-id'], 'self' => false]; $condition = ['id' => $arr['contact-id'], 'self' => false];
} }
DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], $condition); Contact::update(['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], $condition);
} }
// Now do the same for the system wide contacts with uid=0 // Now do the same for the system wide contacts with uid=0
if ($arr['private'] != self::PRIVATE) { if ($arr['private'] != self::PRIVATE) {
DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], Contact::update(['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
['id' => $arr['owner-id']]); ['id' => $arr['owner-id']]);
if ($arr['owner-id'] != $arr['author-id']) { if ($arr['owner-id'] != $arr['author-id']) {
DBA::update('contact', ['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']], Contact::update(['failed' => false, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
['id' => $arr['author-id']]); ['id' => $arr['author-id']]);
} }
} }

View file

@ -234,7 +234,7 @@ class User
$system['closeness'] = 0; $system['closeness'] = 0;
$system['baseurl'] = DI::baseUrl(); $system['baseurl'] = DI::baseUrl();
$system['gsid'] = GServer::getID($system['baseurl']); $system['gsid'] = GServer::getID($system['baseurl']);
DBA::insert('contact', $system); Contact::insert($system);
} }
/** /**

View file

@ -50,7 +50,7 @@ class Note extends BaseApi
DI::mstdnError()->RecordNotFound(); DI::mstdnError()->RecordNotFound();
} }
DBA::update('contact', ['info' => $request['comment']], ['id' => $cdata['user']]); Contact::update(['info' => $request['comment']], ['id' => $cdata['user']]);
System::jsonExit(DI::mstdnRelationship()->createFromContactId($parameters['id'], $uid)->toArray()); System::jsonExit(DI::mstdnRelationship()->createFromContactId($parameters['id'], $uid)->toArray());
} }

View file

@ -144,7 +144,7 @@ class Contact extends BaseModule
$info = Strings::escapeHtml(trim($_POST['info'] ?? '')); $info = Strings::escapeHtml(trim($_POST['info'] ?? ''));
$r = DBA::update('contact', [ $r = Model\Contact::update([
'priority' => $priority, 'priority' => $priority,
'info' => $info, 'info' => $info,
'hidden' => $hidden, 'hidden' => $hidden,
@ -180,7 +180,7 @@ class Contact extends BaseModule
$result = Model\Contact::createFromProbeForUser($contact['uid'], $contact['url'], $contact['network']); $result = Model\Contact::createFromProbeForUser($contact['uid'], $contact['url'], $contact['network']);
if ($result['success']) { if ($result['success']) {
DBA::update('contact', ['subhub' => 1], ['id' => $contact_id]); Model\Contact::update(['subhub' => 1], ['id' => $contact_id]);
} }
// pull feed and consume it, which should subscribe to the hub. // pull feed and consume it, which should subscribe to the hub.

View file

@ -912,7 +912,7 @@ class Processor
$cid = Contact::getIdForURL($activity['actor'], $uid); $cid = Contact::getIdForURL($activity['actor'], $uid);
if (!empty($cid)) { if (!empty($cid)) {
self::switchContact($cid); self::switchContact($cid);
DBA::update('contact', ['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]); Contact::update(['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]);
} }
$item = ['author-id' => Contact::getIdForURL($activity['actor']), $item = ['author-id' => Contact::getIdForURL($activity['actor']),
@ -932,7 +932,7 @@ class Processor
} }
if (empty($contact)) { if (empty($contact)) {
DBA::update('contact', ['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]); Contact::update(['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]);
} }
Logger::notice('Follow user ' . $uid . ' from contact ' . $cid . ' with id ' . $activity['id']); Logger::notice('Follow user ' . $uid . ' from contact ' . $cid . ' with id ' . $activity['id']);
@ -1011,7 +1011,7 @@ class Processor
} }
$condition = ['id' => $cid]; $condition = ['id' => $cid];
DBA::update('contact', $fields, $condition); Contact::update($fields, $condition);
Logger::info('Accept contact request', ['contact' => $cid, 'user' => $uid]); Logger::info('Accept contact request', ['contact' => $cid, 'user' => $uid]);
} }

View file

@ -115,7 +115,7 @@ class Transmitter
$activity_id = ActivityPub\Transmitter::activityIDFromContact($contact['id']); $activity_id = ActivityPub\Transmitter::activityIDFromContact($contact['id']);
$success = ActivityPub\Transmitter::sendActivity('Follow', $url, 0, $activity_id); $success = ActivityPub\Transmitter::sendActivity('Follow', $url, 0, $activity_id);
if ($success) { if ($success) {
DBA::update('contact', ['rel' => Contact::FRIEND], ['id' => $contact['id']]); Contact::update(['rel' => Contact::FRIEND], ['id' => $contact['id']]);
} }
return $success; return $success;
@ -137,7 +137,7 @@ class Transmitter
$success = self::sendContactUndo($url, $contact['id'], 0); $success = self::sendContactUndo($url, $contact['id'], 0);
if ($success || $force) { if ($success || $force) {
DBA::update('contact', ['rel' => Contact::NOTHING], ['id' => $contact['id']]); Contact::update(['rel' => Contact::NOTHING], ['id' => $contact['id']]);
} }
return $success; return $success;

View file

@ -1215,12 +1215,12 @@ class DFRN
'xmpp' => $contact['xmpp'], 'name-date' => DateTimeFormat::utc($contact['name-date']), 'xmpp' => $contact['xmpp'], 'name-date' => DateTimeFormat::utc($contact['name-date']),
'unsearchable' => $contact['hidden'], 'uri-date' => DateTimeFormat::utc($contact['uri-date'])]; 'unsearchable' => $contact['hidden'], 'uri-date' => DateTimeFormat::utc($contact['uri-date'])];
DBA::update('contact', $fields, ['id' => $contact['id'], 'network' => $contact['network']], $contact_old); Contact::update($fields, ['id' => $contact['id'], 'network' => $contact['network']], $contact_old);
// Update the public contact. Don't set the "hidden" value, this is used differently for public contacts // Update the public contact. Don't set the "hidden" value, this is used differently for public contacts
unset($fields['hidden']); unset($fields['hidden']);
$condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($contact_old['url'])]; $condition = ['uid' => 0, 'nurl' => Strings::normaliseLink($contact_old['url'])];
DBA::update('contact', $fields, $condition, true); Contact::update($fields, $condition, true);
Contact::updateAvatar($contact['id'], $author['avatar']); Contact::updateAvatar($contact['id'], $author['avatar']);
@ -1400,7 +1400,7 @@ class DFRN
'poll' => $relocate["poll"], 'site-pubkey' => $relocate["sitepubkey"]]; 'poll' => $relocate["poll"], 'site-pubkey' => $relocate["sitepubkey"]];
$condition = ["(`id` = ?) OR (`nurl` = ?)", $importer["id"], Strings::normaliseLink($old["url"])]; $condition = ["(`id` = ?) OR (`nurl` = ?)", $importer["id"], Strings::normaliseLink($old["url"])];
DBA::update('contact', $fields, $condition); Contact::update($fields, $condition);
Contact::updateAvatar($importer["id"], $relocate["avatar"], true); Contact::updateAvatar($importer["id"], $relocate["avatar"], true);
@ -2200,36 +2200,36 @@ class DFRN
$accounttype = intval(XML::getFirstNodeValue($xpath, "/atom:feed/dfrn:account_type/text()")); $accounttype = intval(XML::getFirstNodeValue($xpath, "/atom:feed/dfrn:account_type/text()"));
if ($accounttype != $importer["contact-type"]) { if ($accounttype != $importer["contact-type"]) {
DBA::update('contact', ['contact-type' => $accounttype], ['id' => $importer['id']]); Contact::update(['contact-type' => $accounttype], ['id' => $importer['id']]);
// Updating the public contact as well // Updating the public contact as well
DBA::update('contact', ['contact-type' => $accounttype], ['uid' => 0, 'nurl' => $importer['nurl']]); Contact::update(['contact-type' => $accounttype], ['uid' => 0, 'nurl' => $importer['nurl']]);
} }
// A forum contact can either have set "forum" or "prv" - but not both // A forum contact can either have set "forum" or "prv" - but not both
if ($accounttype == User::ACCOUNT_TYPE_COMMUNITY) { if ($accounttype == User::ACCOUNT_TYPE_COMMUNITY) {
// It's a forum, so either set the public or private forum flag // It's a forum, so either set the public or private forum flag
$condition = ['(`forum` != ? OR `prv` != ?) AND `id` = ?', $forum, !$forum, $importer['id']]; $condition = ['(`forum` != ? OR `prv` != ?) AND `id` = ?', $forum, !$forum, $importer['id']];
DBA::update('contact', ['forum' => $forum, 'prv' => !$forum], $condition); Contact::update(['forum' => $forum, 'prv' => !$forum], $condition);
// Updating the public contact as well // Updating the public contact as well
$condition = ['(`forum` != ? OR `prv` != ?) AND `uid` = 0 AND `nurl` = ?', $forum, !$forum, $importer['nurl']]; $condition = ['(`forum` != ? OR `prv` != ?) AND `uid` = 0 AND `nurl` = ?', $forum, !$forum, $importer['nurl']];
DBA::update('contact', ['forum' => $forum, 'prv' => !$forum], $condition); Contact::update(['forum' => $forum, 'prv' => !$forum], $condition);
} else { } else {
// It's not a forum, so remove the flags // It's not a forum, so remove the flags
$condition = ['(`forum` OR `prv`) AND `id` = ?', $importer['id']]; $condition = ['(`forum` OR `prv`) AND `id` = ?', $importer['id']];
DBA::update('contact', ['forum' => false, 'prv' => false], $condition); Contact::update(['forum' => false, 'prv' => false], $condition);
// Updating the public contact as well // Updating the public contact as well
$condition = ['(`forum` OR `prv`) AND `uid` = 0 AND `nurl` = ?', $importer['nurl']]; $condition = ['(`forum` OR `prv`) AND `uid` = 0 AND `nurl` = ?', $importer['nurl']];
DBA::update('contact', ['forum' => false, 'prv' => false], $condition); Contact::update(['forum' => false, 'prv' => false], $condition);
} }
} elseif ($forum != $importer["forum"]) { // Deprecated since 3.5.1 } elseif ($forum != $importer["forum"]) { // Deprecated since 3.5.1
$condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]]; $condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]];
DBA::update('contact', ['forum' => $forum], $condition); Contact::update(['forum' => $forum], $condition);
// Updating the public contact as well // Updating the public contact as well
$condition = ['`forum` != ? AND `uid` = 0 AND `nurl` = ?', $forum, $importer['nurl']]; $condition = ['`forum` != ? AND `uid` = 0 AND `nurl` = ?', $forum, $importer['nurl']];
DBA::update('contact', ['forum' => $forum], $condition); Contact::update(['forum' => $forum], $condition);
} }

View file

@ -1367,7 +1367,7 @@ class Diaspora
'notify' => $data['notify'], 'poll' => $data['poll'], 'notify' => $data['notify'], 'poll' => $data['poll'],
'network' => $data['network']]; 'network' => $data['network']];
DBA::update('contact', $fields, ['addr' => $old_handle]); Contact::update($fields, ['addr' => $old_handle]);
Logger::log('Contacts are updated.'); Logger::log('Contacts are updated.');
@ -2129,7 +2129,7 @@ class Diaspora
$fields['bd'] = $birthday; $fields['bd'] = $birthday;
} }
DBA::update('contact', $fields, ['id' => $contact['id']]); Contact::update($fields, ['id' => $contact['id']]);
Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG); Logger::log("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], Logger::DEBUG);

View file

@ -732,7 +732,7 @@ class Feed
if ($contact['rating'] != $priority) { if ($contact['rating'] != $priority) {
Logger::notice('Adjusting priority', ['old' => $contact['rating'], 'new' => $priority, 'id' => $contact['id'], 'uid' => $contact['uid'], 'url' => $contact['url']]); Logger::notice('Adjusting priority', ['old' => $contact['rating'], 'new' => $priority, 'id' => $contact['id'], 'uid' => $contact['uid'], 'url' => $contact['url']]);
DBA::update('contact', ['rating' => $priority], ['id' => $contact['id']]); Contact::update(['rating' => $priority], ['id' => $contact['id']]);
} }
} }

View file

@ -209,7 +209,7 @@ class OStatus
$contact['name-date'] = DateTimeFormat::utcNow(); $contact['name-date'] = DateTimeFormat::utcNow();
DBA::update('contact', $contact, ['id' => $contact["id"]], $current); Contact::update($contact, ['id' => $contact["id"]], $current);
if (!empty($author["author-avatar"]) && ($author["author-avatar"] != $current['avatar'])) { if (!empty($author["author-avatar"]) && ($author["author-avatar"] != $current['avatar'])) {
Logger::log("Update profile picture for contact ".$contact["id"], Logger::DEBUG); Logger::log("Update profile picture for contact ".$contact["id"], Logger::DEBUG);
@ -230,7 +230,7 @@ class OStatus
'about' => $contact["about"], 'location' => $contact["location"], 'about' => $contact["about"], 'location' => $contact["location"],
'success_update' => DateTimeFormat::utcNow(), 'last-update' => DateTimeFormat::utcNow()]; 'success_update' => DateTimeFormat::utcNow(), 'last-update' => DateTimeFormat::utcNow()];
DBA::update('contact', $fields, ['id' => $cid], $old_contact); Contact::update($fields, ['id' => $cid], $old_contact);
// Update the avatar // Update the avatar
if (!empty($author["author-avatar"])) { if (!empty($author["author-avatar"])) {

View file

@ -166,7 +166,7 @@ class Relay
$fields['updated'] = DateTimeFormat::utcNow(); $fields['updated'] = DateTimeFormat::utcNow();
Logger::info('Update relay contact', ['server' => $gserver['url'], 'id' => $old['id'], 'fields' => $fields]); Logger::info('Update relay contact', ['server' => $gserver['url'], 'id' => $old['id'], 'fields' => $fields]);
DBA::update('contact', $fields, ['id' => $old['id']], $old); Contact::update($fields, ['id' => $old['id']], $old);
} else { } else {
$default = ['created' => DateTimeFormat::utcNow(), $default = ['created' => DateTimeFormat::utcNow(),
'name' => 'relay', 'nick' => 'relay', 'url' => $gserver['url'], 'name' => 'relay', 'nick' => 'relay', 'url' => $gserver['url'],

View file

@ -184,6 +184,7 @@ class ExpirePosts
AND NOT EXISTS(SELECT `external-id` FROM `post-user` WHERE `external-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `external-id` FROM `post-user` WHERE `external-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `mail` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `mail` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `event` WHERE `uri-id` = `item-uri`.`id`) AND NOT EXISTS(SELECT `uri-id` FROM `event` 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 `fcontact` WHERE `uri-id` = `item-uri`.`id`)

View file

@ -703,7 +703,7 @@ class Notifier
} }
while($contact = DBA::fetch($contacts_stmt)) { while($contact = DBA::fetch($contacts_stmt)) {
Contact::terminateFriendship($owner, $contact, true); Protocol::terminateFriendship($owner, $contact, true);
} }
DBA::close($contacts_stmt); DBA::close($contacts_stmt);

View file

@ -82,7 +82,7 @@ class OnePoll
Logger::warning('No self contact for user', ['uid' => $importer_uid]); Logger::warning('No self contact for user', ['uid' => $importer_uid]);
// set the last-update so we don't keep polling // set the last-update so we don't keep polling
DBA::update('contact', ['last-update' => $updated], ['id' => $contact['id']]); Contact::update(['last-update' => $updated], ['id' => $contact['id']]);
return; return;
} }
@ -122,16 +122,16 @@ class OnePoll
{ {
if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL, Protocol::OSTATUS])) { if (in_array($contact['network'], [Protocol::FEED, Protocol::MAIL, Protocol::OSTATUS])) {
// Update the user's contact // Update the user's contact
DBA::update('contact', $fields, ['id' => $contact['id']]); Contact::update($fields, ['id' => $contact['id']]);
// Update the public contact // Update the public contact
DBA::update('contact', $fields, ['uid' => 0, 'nurl' => $contact['nurl']]); Contact::update($fields, ['uid' => 0, 'nurl' => $contact['nurl']]);
// Update the rest of the contacts that aren't polled // Update the rest of the contacts that aren't polled
DBA::update('contact', $fields, ['rel' => Contact::FOLLOWER, 'nurl' => $contact['nurl']]); Contact::update($fields, ['rel' => Contact::FOLLOWER, 'nurl' => $contact['nurl']]);
} else { } else {
// Update all contacts // Update all contacts
DBA::update('contact', $fields, ['nurl' => $contact['nurl']]); Contact::update($fields, ['nurl' => $contact['nurl']]);
} }
} }
@ -456,7 +456,7 @@ class OnePoll
Logger::info('Hub subscription start', ['mode' => $hubmode, 'name' => $contact['name'], 'hub' => $url, 'endpoint' => $push_url, 'verifier' => $verify_token]); Logger::info('Hub subscription start', ['mode' => $hubmode, 'name' => $contact['name'], 'hub' => $url, 'endpoint' => $push_url, 'verifier' => $verify_token]);
if (!strlen($contact['hub-verify']) || ($contact['hub-verify'] != $verify_token)) { if (!strlen($contact['hub-verify']) || ($contact['hub-verify'] != $verify_token)) {
DBA::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]); Contact::update(['hub-verify' => $verify_token], ['id' => $contact['id']]);
} }
$postResult = DI::httpClient()->post($url, $params); $postResult = DI::httpClient()->post($url, $params);

View file

@ -44,7 +44,7 @@ class RemoveUnusedAvatars
$count = 0; $count = 0;
$contacts = DBA::select('contact', ['id'], $condition); $contacts = DBA::select('contact', ['id'], $condition);
while ($contact = DBA::fetch($contacts)) { while ($contact = DBA::fetch($contacts)) {
DBA::update('contact', ['photo' => '', 'thumb' => '', 'micro' => ''], ['id' => $contact['id']]); Contact::update(['photo' => '', 'thumb' => '', 'micro' => ''], ['id' => $contact['id']]);
Photo::delete(['contact-id' => $contact['id'], 'album' => Photo::CONTACT_PHOTOS]); Photo::delete(['contact-id' => $contact['id'], 'album' => Photo::CONTACT_PHOTOS]);
if ((++$count % 1000) == 0) { if ((++$count % 1000) == 0) {
if (!Worker::isInMaintenanceWindow()) { if (!Worker::isInMaintenanceWindow()) {

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', 1434); define('DB_UPDATE_VERSION', 1435);
} }
return [ return [
@ -172,29 +172,16 @@ return [
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
"created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"updated" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => "Date of last contact update"], "updated" => ["type" => "datetime", "default" => DBA::NULL_DATETIME, "comment" => "Date of last contact update"],
"self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 if the contact is the user him/her self"],
"remote_self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"rel" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "The kind of the relation between the user and the contact"],
"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Network of the contact"], "network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Network of the contact"],
"protocol" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Protocol of the contact"],
"name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Name that this contact is known by"], "name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Name that this contact is known by"],
"nick" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Nick- and user name of the contact"], "nick" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Nick- and user name of the contact"],
"location" => ["type" => "varchar(255)", "default" => "", "comment" => ""], "location" => ["type" => "varchar(255)", "default" => "", "comment" => ""],
"about" => ["type" => "text", "comment" => ""], "about" => ["type" => "text", "comment" => ""],
"keywords" => ["type" => "text", "comment" => "public keywords (interests) of the contact"], "keywords" => ["type" => "text", "comment" => "public keywords (interests) of the contact"],
"gender" => ["type" => "varchar(32)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"xmpp" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "XMPP address"], "xmpp" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "XMPP address"],
"matrix" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Matrix address"], "matrix" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Matrix address"],
"attag" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"photo" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo of the contact"],
"thumb" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (thumb size)"],
"micro" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (micro size)"],
"header" => ["type" => "varchar(255)", "comment" => "Header picture"], "header" => ["type" => "varchar(255)", "comment" => "Header picture"],
"site-pubkey" => ["type" => "text", "comment" => "Deprecated"],
"issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"url" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "url" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"nurl" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "nurl" => ["type" => "varchar(255)", "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 contact url"], "uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact url"],
@ -203,54 +190,70 @@ return [
"pubkey" => ["type" => "text", "comment" => "RSA public key 4096 bit"], "pubkey" => ["type" => "text", "comment" => "RSA public key 4096 bit"],
"prvkey" => ["type" => "text", "comment" => "RSA private key 4096 bit"], "prvkey" => ["type" => "text", "comment" => "RSA private key 4096 bit"],
"batch" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "batch" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"request" => ["type" => "varchar(255)", "comment" => ""],
"notify" => ["type" => "varchar(255)", "comment" => ""], "notify" => ["type" => "varchar(255)", "comment" => ""],
"poll" => ["type" => "varchar(255)", "comment" => ""], "poll" => ["type" => "varchar(255)", "comment" => ""],
"confirm" => ["type" => "varchar(255)", "comment" => ""],
"subscribe" => ["type" => "varchar(255)", "comment" => ""], "subscribe" => ["type" => "varchar(255)", "comment" => ""],
"poco" => ["type" => "varchar(255)", "comment" => ""],
"aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"usehub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"subhub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"hub-verify" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"last-update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last try to update the contact info"], "last-update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last try to update the contact info"],
"success_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last successful contact update"], "success_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last successful contact update"],
"failure_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last failed update"], "failure_update" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last failed update"],
"failed" => ["type" => "boolean", "comment" => "Connection failed"], "failed" => ["type" => "boolean", "comment" => "Connection failed"],
"name-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"uri-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"avatar-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"term-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "term-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"last-item" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last post"], "last-item" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last post"],
"last-discovery" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last follower discovery"], "last-discovery" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last follower discovery"],
"priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"blocked" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Node-wide block status"], "blocked" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Node-wide block status"],
"block_reason" => ["type" => "text", "comment" => "Node-wide block reason"], "block_reason" => ["type" => "text", "comment" => "Node-wide block reason"],
"readonly" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "posts of the contact are readonly"], "readonly" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "posts of the contact are readonly"],
"writable" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "contact-type" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Person, organisation, news, community, relay"],
"forum" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "contact is a forum"], "manually-approve" => ["type" => "boolean", "comment" => "Contact requests have to be approved manually"],
"prv" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "contact is a private group"],
"contact-type" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => ""],
"manually-approve" => ["type" => "boolean", "comment" => ""],
"hidden" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"archive" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "archive" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"pending" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => ""],
"deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact has been deleted"],
"rating" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => ""],
"unsearchable" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact prefers to not be searchable"], "unsearchable" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact prefers to not be searchable"],
"sensitive" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact posts sensitive content"], "sensitive" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact posts sensitive content"],
"baseurl" => ["type" => "varchar(255)", "default" => "", "comment" => "baseurl of the contact"], "baseurl" => ["type" => "varchar(255)", "default" => "", "comment" => "baseurl of the contact"],
"gsid" => ["type" => "int unsigned", "foreign" => ["gserver" => "id", "on delete" => "restrict"], "comment" => "Global Server ID"], "gsid" => ["type" => "int unsigned", "foreign" => ["gserver" => "id", "on delete" => "restrict"], "comment" => "Global Server ID"],
"reason" => ["type" => "text", "comment" => ""],
"closeness" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "99", "comment" => ""],
"info" => ["type" => "mediumtext", "comment" => ""],
"profile-id" => ["type" => "int unsigned", "comment" => "Deprecated"],
"bdyear" => ["type" => "varchar(4)", "not null" => "1", "default" => "", "comment" => ""],
"bd" => ["type" => "date", "not null" => "1", "default" => DBA::NULL_DATE, "comment" => ""], "bd" => ["type" => "date", "not null" => "1", "default" => DBA::NULL_DATE, "comment" => ""],
// User depending fields
"reason" => ["type" => "text", "comment" => ""],
"self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 if the contact is the user him/her self"],
"remote_self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"rel" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "The kind of the relation between the user and the contact"],
"protocol" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Protocol of the contact"],
"subhub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"hub-verify" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"rating" => ["type" => "tinyint", "not null" => "1", "default" => "0", "comment" => "Automatically detected feed poll frequency"],
"priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "Feed poll priority"],
"attag" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"hidden" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"pending" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Contact request is pending"],
"deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Contact has been deleted"],
"info" => ["type" => "mediumtext", "comment" => ""],
"notify_new_posts" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "notify_new_posts" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"fetch_further_information" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""], "fetch_further_information" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"ffi_keyword_denylist" => ["type" => "text", "comment" => ""], "ffi_keyword_denylist" => ["type" => "text", "comment" => ""],
// Deprecated, but still in use
"photo" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo of the contact"],
"thumb" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (thumb size)"],
"micro" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (micro size)"],
"name-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"uri-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"avatar-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"request" => ["type" => "varchar(255)", "comment" => ""],
"confirm" => ["type" => "varchar(255)", "comment" => ""],
"poco" => ["type" => "varchar(255)", "comment" => ""],
"writable" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"forum" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "contact is a forum. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = false instead"],
"prv" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "contact is a private group. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = true instead"],
"bdyear" => ["type" => "varchar(4)", "not null" => "1", "default" => "", "comment" => ""],
// Deprecated fields that aren't in use anymore
"site-pubkey" => ["type" => "text", "comment" => "Deprecated"],
"gender" => ["type" => "varchar(32)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"usehub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"closeness" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "99", "comment" => "Deprecated"],
"profile-id" => ["type" => "int unsigned", "comment" => "Deprecated"],
], ],
"indexes" => [ "indexes" => [
"PRIMARY" => ["id"], "PRIMARY" => ["id"],
@ -1531,13 +1534,27 @@ return [
"fields" => [ "fields" => [
"cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["contact" => "id"], "comment" => "Contact id of the linked public contact"], "cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["contact" => "id"], "comment" => "Contact id of the linked public contact"],
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "User id"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "User id"],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact url"],
"blocked" => ["type" => "boolean", "comment" => "Contact is completely blocked for this user"], "blocked" => ["type" => "boolean", "comment" => "Contact is completely blocked for this user"],
"ignored" => ["type" => "boolean", "comment" => "Posts from this contact are ignored"], "ignored" => ["type" => "boolean", "comment" => "Posts from this contact are ignored"],
"collapsed" => ["type" => "boolean", "comment" => "Posts from this contact are collapsed"] "collapsed" => ["type" => "boolean", "comment" => "Posts from this contact are collapsed"],
"pending" => ["type" => "boolean", "comment" => ""],
"rel" => ["type" => "tinyint unsigned", "comment" => "The kind of the relation between the user and the contact"],
"info" => ["type" => "mediumtext", "comment" => ""],
"notify_new_posts" => ["type" => "boolean", "comment" => ""],
"remote_self" => ["type" => "boolean", "comment" => ""],
"fetch_further_information" => ["type" => "tinyint unsigned", "comment" => ""],
"ffi_keyword_denylist" => ["type" => "text", "comment" => ""],
"subhub" => ["type" => "boolean", "comment" => ""],
"hub-verify" => ["type" => "varchar(255)", "comment" => ""],
"protocol" => ["type" => "char(4)", "comment" => "Protocol of the contact"],
"rating" => ["type" => "tinyint", "comment" => "Automatically detected feed poll frequency"],
"priority" => ["type" => "tinyint unsigned", "comment" => "Feed poll priority"],
], ],
"indexes" => [ "indexes" => [
"PRIMARY" => ["uid", "cid"], "PRIMARY" => ["uid", "cid"],
"cid" => ["cid"], "cid" => ["cid"],
"uri-id_uid" => ["UNIQUE", "uri-id", "uid"],
] ]
], ],
"worker-ipc" => [ "worker-ipc" => [

View file

@ -711,7 +711,6 @@
"location" => ["contact", "location"], "location" => ["contact", "location"],
"about" => ["contact", "about"], "about" => ["contact", "about"],
"keywords" => ["contact", "keywords"], "keywords" => ["contact", "keywords"],
"gender" => ["contact", "gender"],
"xmpp" => ["contact", "xmpp"], "xmpp" => ["contact", "xmpp"],
"matrix" => ["contact", "matrix"], "matrix" => ["contact", "matrix"],
"attag" => ["contact", "attag"], "attag" => ["contact", "attag"],
@ -733,7 +732,6 @@
"poll" => ["contact", "poll"], "poll" => ["contact", "poll"],
"confirm" => ["contact", "confirm"], "confirm" => ["contact", "confirm"],
"poco" => ["contact", "poco"], "poco" => ["contact", "poco"],
"usehub" => ["contact", "usehub"],
"subhub" => ["contact", "subhub"], "subhub" => ["contact", "subhub"],
"hub-verify" => ["contact", "hub-verify"], "hub-verify" => ["contact", "hub-verify"],
"last-update" => ["contact", "last-update"], "last-update" => ["contact", "last-update"],
@ -762,9 +760,7 @@
"sensitive" => ["contact", "sensitive"], "sensitive" => ["contact", "sensitive"],
"baseurl" => ["contact", "baseurl"], "baseurl" => ["contact", "baseurl"],
"reason" => ["contact", "reason"], "reason" => ["contact", "reason"],
"closeness" => ["contact", "closeness"],
"info" => ["contact", "info"], "info" => ["contact", "info"],
"profile-id" => ["contact", "profile-id"],
"bdyear" => ["contact", "bdyear"], "bdyear" => ["contact", "bdyear"],
"bd" => ["contact", "bd"], "bd" => ["contact", "bd"],
"notify_new_posts" => ["contact", "notify_new_posts"], "notify_new_posts" => ["contact", "notify_new_posts"],
@ -934,7 +930,7 @@
"network" => ["contact", "network"], "network" => ["contact", "network"],
"protocol" => ["ucontact", "protocol"], "protocol" => ["ucontact", "protocol"],
"location" => ["contact", "location"], "location" => ["contact", "location"],
"attag" => ["contact", "attag"], "attag" => ["ucontact", "attag"],
"pubkey" => ["contact", "pubkey"], "pubkey" => ["contact", "pubkey"],
"prvkey" => ["contact", "prvkey"], "prvkey" => ["contact", "prvkey"],
"subscribe" => ["contact", "subscribe"], "subscribe" => ["contact", "subscribe"],
@ -950,7 +946,7 @@
"sensitive" => ["contact", "sensitive"], "sensitive" => ["contact", "sensitive"],
"baseurl" => ["contact", "baseurl"], "baseurl" => ["contact", "baseurl"],
"gsid" => ["contact", "gsid"], "gsid" => ["contact", "gsid"],
"info" => ["contact", "info"], "info" => ["ucontact", "info"],
"bdyear" => ["contact", "bdyear"], "bdyear" => ["contact", "bdyear"],
"bd" => ["contact", "bd"], "bd" => ["contact", "bd"],
"poco" => ["contact", "poco"], "poco" => ["contact", "poco"],

View file

@ -998,3 +998,11 @@ function update_1434()
return Update::SUCCESS; return Update::SUCCESS;
} }
function update_1435()
{
$contacts = DBA::select('contact', [], ["`uid` != ?", 0]);
while ($contact = DBA::fetch($contacts)) {
Contact\User::insertForContactArray($contact);
}
}

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2021.12-dev\n" "Project-Id-Version: 2021.12-dev\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-10-02 14:25+0000\n" "POT-Creation-Date: 2021-10-02 15:13+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -37,7 +37,7 @@ msgstr[1] ""
msgid "Monthly posting limit of %d post reached. The post was rejected." msgid "Monthly posting limit of %d post reached. The post was rejected."
msgstr "" msgstr ""
#: include/api.php:4429 mod/photos.php:89 mod/photos.php:198 mod/photos.php:626 #: include/api.php:4430 mod/photos.php:89 mod/photos.php:198 mod/photos.php:626
#: mod/photos.php:1035 mod/photos.php:1052 mod/photos.php:1599 #: mod/photos.php:1035 mod/photos.php:1052 mod/photos.php:1599
#: src/Model/User.php:1169 src/Model/User.php:1177 src/Model/User.php:1185 #: src/Model/User.php:1169 src/Model/User.php:1177 src/Model/User.php:1185
#: src/Module/Settings/Profile/Photo/Crop.php:101 #: src/Module/Settings/Profile/Photo/Crop.php:101
@ -717,7 +717,7 @@ msgid "OStatus support is disabled. Contact can't be added."
msgstr "" msgstr ""
#: mod/follow.php:138 src/Content/Item.php:463 src/Content/Widget.php:76 #: mod/follow.php:138 src/Content/Item.php:463 src/Content/Widget.php:76
#: src/Model/Contact.php:1051 src/Model/Contact.php:1064 #: src/Model/Contact.php:1046 src/Model/Contact.php:1059
#: view/theme/vier/theme.php:172 #: view/theme/vier/theme.php:172
msgid "Connect/Follow" msgid "Connect/Follow"
msgstr "" msgstr ""
@ -2277,11 +2277,11 @@ msgid ""
"select \"Export account\"" "select \"Export account\""
msgstr "" msgstr ""
#: mod/unfollow.php:65 mod/unfollow.php:129 #: mod/unfollow.php:65 mod/unfollow.php:135
msgid "You aren't following this contact." msgid "You aren't following this contact."
msgstr "" msgstr ""
#: mod/unfollow.php:71 mod/unfollow.php:135 #: mod/unfollow.php:71
msgid "Unfollowing is currently not supported by your network." msgid "Unfollowing is currently not supported by your network."
msgstr "" msgstr ""
@ -2289,6 +2289,24 @@ msgstr ""
msgid "Disconnect/Unfollow" msgid "Disconnect/Unfollow"
msgstr "" msgstr ""
#: mod/unfollow.php:149
msgid "Unfollowing is currently not supported by this contact's network."
msgstr ""
#: mod/unfollow.php:153
msgid ""
"Unable to unfollow this contact, please retry in a few minutes or contact "
"your administrator."
msgstr ""
#: mod/unfollow.php:157
msgid "Contact was successfully unfollowed"
msgstr ""
#: mod/unfollow.php:161
msgid "Unable to unfollow this contact, please contact your administrator"
msgstr ""
#: mod/wall_attach.php:42 mod/wall_attach.php:49 mod/wall_attach.php:87 #: mod/wall_attach.php:42 mod/wall_attach.php:49 mod/wall_attach.php:87
#: mod/wall_upload.php:52 mod/wall_upload.php:63 mod/wall_upload.php:108 #: mod/wall_upload.php:52 mod/wall_upload.php:63 mod/wall_upload.php:108
#: mod/wall_upload.php:159 mod/wall_upload.php:162 #: mod/wall_upload.php:159 mod/wall_upload.php:162
@ -2407,12 +2425,12 @@ msgstr ""
msgid "Addon already disabled" msgid "Addon already disabled"
msgstr "" msgstr ""
#: src/Console/ArchiveContact.php:105 #: src/Console/ArchiveContact.php:106
#, php-format #, php-format
msgid "Could not find any unarchived contact entry for this URL (%s)" msgid "Could not find any unarchived contact entry for this URL (%s)"
msgstr "" msgstr ""
#: src/Console/ArchiveContact.php:108 #: src/Console/ArchiveContact.php:109
msgid "The contact entries have been archived" msgid "The contact entries have been archived"
msgstr "" msgstr ""
@ -2985,31 +3003,31 @@ msgstr ""
msgid "Follow Thread" msgid "Follow Thread"
msgstr "" msgstr ""
#: src/Content/Item.php:443 src/Model/Contact.php:1056 #: src/Content/Item.php:443 src/Model/Contact.php:1051
msgid "View Status" msgid "View Status"
msgstr "" msgstr ""
#: src/Content/Item.php:444 src/Content/Item.php:466 src/Model/Contact.php:982 #: src/Content/Item.php:444 src/Content/Item.php:466 src/Model/Contact.php:977
#: src/Model/Contact.php:1048 src/Model/Contact.php:1057 #: src/Model/Contact.php:1043 src/Model/Contact.php:1052
#: src/Module/Directory.php:160 src/Module/Settings/Profile/Index.php:223 #: src/Module/Directory.php:160 src/Module/Settings/Profile/Index.php:223
msgid "View Profile" msgid "View Profile"
msgstr "" msgstr ""
#: src/Content/Item.php:445 src/Model/Contact.php:1058 #: src/Content/Item.php:445 src/Model/Contact.php:1053
msgid "View Photos" msgid "View Photos"
msgstr "" msgstr ""
#: src/Content/Item.php:446 src/Model/Contact.php:1049 #: src/Content/Item.php:446 src/Model/Contact.php:1044
#: src/Model/Contact.php:1059 #: src/Model/Contact.php:1054
msgid "Network Posts" msgid "Network Posts"
msgstr "" msgstr ""
#: src/Content/Item.php:447 src/Model/Contact.php:1050 #: src/Content/Item.php:447 src/Model/Contact.php:1045
#: src/Model/Contact.php:1060 #: src/Model/Contact.php:1055
msgid "View Contact" msgid "View Contact"
msgstr "" msgstr ""
#: src/Content/Item.php:448 src/Model/Contact.php:1062 #: src/Content/Item.php:448 src/Model/Contact.php:1057
msgid "Send PM" msgid "Send PM"
msgstr "" msgstr ""
@ -3032,7 +3050,7 @@ msgstr ""
msgid "Languages" msgid "Languages"
msgstr "" msgstr ""
#: src/Content/Item.php:458 src/Model/Contact.php:1063 #: src/Content/Item.php:458 src/Model/Contact.php:1058
msgid "Poke" msgid "Poke"
msgstr "" msgstr ""
@ -3482,7 +3500,7 @@ msgstr ""
msgid "Organisations" msgid "Organisations"
msgstr "" msgstr ""
#: src/Content/Widget.php:529 src/Model/Contact.php:1487 #: src/Content/Widget.php:529 src/Model/Contact.php:1481
msgid "News" msgid "News"
msgstr "" msgstr ""
@ -4346,85 +4364,85 @@ msgstr ""
msgid "Legacy module file not found: %s" msgid "Legacy module file not found: %s"
msgstr "" msgstr ""
#: src/Model/Contact.php:1052 src/Model/Contact.php:1065 #: src/Model/Contact.php:1047 src/Model/Contact.php:1060
msgid "UnFollow" msgid "UnFollow"
msgstr "" msgstr ""
#: src/Model/Contact.php:1061 #: src/Model/Contact.php:1056
msgid "Drop Contact" msgid "Drop Contact"
msgstr "" msgstr ""
#: src/Model/Contact.php:1071 src/Module/Admin/Users/Pending.php:107 #: src/Model/Contact.php:1066 src/Module/Admin/Users/Pending.php:107
#: src/Module/Notifications/Introductions.php:111 #: src/Module/Notifications/Introductions.php:111
#: src/Module/Notifications/Introductions.php:183 #: src/Module/Notifications/Introductions.php:183
msgid "Approve" msgid "Approve"
msgstr "" msgstr ""
#: src/Model/Contact.php:1483 #: src/Model/Contact.php:1477
msgid "Organisation" msgid "Organisation"
msgstr "" msgstr ""
#: src/Model/Contact.php:1491 #: src/Model/Contact.php:1485
msgid "Forum" msgid "Forum"
msgstr "" msgstr ""
#: src/Model/Contact.php:2347 #: src/Model/Contact.php:2341
msgid "Disallowed profile URL." msgid "Disallowed profile URL."
msgstr "" msgstr ""
#: src/Model/Contact.php:2352 src/Module/Friendica.php:81 #: src/Model/Contact.php:2346 src/Module/Friendica.php:81
msgid "Blocked domain" msgid "Blocked domain"
msgstr "" msgstr ""
#: src/Model/Contact.php:2357 #: src/Model/Contact.php:2351
msgid "Connect URL missing." msgid "Connect URL missing."
msgstr "" msgstr ""
#: src/Model/Contact.php:2366 #: src/Model/Contact.php:2360
msgid "" msgid ""
"The contact could not be added. Please check the relevant network " "The contact could not be added. Please check the relevant network "
"credentials in your Settings -> Social Networks page." "credentials in your Settings -> Social Networks page."
msgstr "" msgstr ""
#: src/Model/Contact.php:2403 #: src/Model/Contact.php:2397
msgid "The profile address specified does not provide adequate information." msgid "The profile address specified does not provide adequate information."
msgstr "" msgstr ""
#: src/Model/Contact.php:2405 #: src/Model/Contact.php:2399
msgid "No compatible communication protocols or feeds were discovered." msgid "No compatible communication protocols or feeds were discovered."
msgstr "" msgstr ""
#: src/Model/Contact.php:2408 #: src/Model/Contact.php:2402
msgid "An author or name was not found." msgid "An author or name was not found."
msgstr "" msgstr ""
#: src/Model/Contact.php:2411 #: src/Model/Contact.php:2405
msgid "No browser URL could be matched to this address." msgid "No browser URL could be matched to this address."
msgstr "" msgstr ""
#: src/Model/Contact.php:2414 #: src/Model/Contact.php:2408
msgid "" msgid ""
"Unable to match @-style Identity Address with a known protocol or email " "Unable to match @-style Identity Address with a known protocol or email "
"contact." "contact."
msgstr "" msgstr ""
#: src/Model/Contact.php:2415 #: src/Model/Contact.php:2409
msgid "Use mailto: in front of address to force email check." msgid "Use mailto: in front of address to force email check."
msgstr "" msgstr ""
#: src/Model/Contact.php:2421 #: src/Model/Contact.php:2415
msgid "" msgid ""
"The profile address specified belongs to a network which has been disabled " "The profile address specified belongs to a network which has been disabled "
"on this site." "on this site."
msgstr "" msgstr ""
#: src/Model/Contact.php:2426 #: src/Model/Contact.php:2420
msgid "" msgid ""
"Limited profile. This person will be unable to receive direct/personal " "Limited profile. This person will be unable to receive direct/personal "
"notifications from you." "notifications from you."
msgstr "" msgstr ""
#: src/Model/Contact.php:2485 #: src/Model/Contact.php:2479
msgid "Unable to retrieve contact information." msgid "Unable to retrieve contact information."
msgstr "" msgstr ""