2017-11-19 22:55:28 +01:00
< ? php
/**
2020-02-09 15:45:36 +01:00
* @ copyright Copyright ( C ) 2020 , Friendica
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2017-11-19 22:55:28 +01:00
*/
2020-02-09 15:45:36 +01:00
2017-12-07 15:04:24 +01:00
namespace Friendica\Model ;
2017-11-19 22:55:28 +01:00
2019-08-15 17:23:00 +02:00
use Friendica\App\BaseURL ;
2018-10-24 08:15:24 +02:00
use Friendica\Content\Pager ;
2018-11-10 14:18:16 +01:00
use Friendica\Core\Hook ;
2018-10-29 22:20:46 +01:00
use Friendica\Core\Logger ;
2018-08-11 22:40:44 +02:00
use Friendica\Core\Protocol ;
2019-09-28 20:09:11 +02:00
use Friendica\Core\Session ;
2019-10-24 09:06:22 +02:00
use Friendica\Core\System ;
2017-11-19 22:55:28 +01:00
use Friendica\Core\Worker ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2019-12-15 22:34:11 +01:00
use Friendica\DI ;
2020-02-04 22:03:45 +01:00
use Friendica\Model\Notify\Type ;
2020-03-09 16:11:06 +01:00
use Friendica\Network\HTTPException ;
2018-01-25 02:47:33 +01:00
use Friendica\Network\Probe ;
2019-10-24 00:25:43 +02:00
use Friendica\Protocol\Activity ;
2018-09-15 20:54:45 +02:00
use Friendica\Protocol\ActivityPub ;
2018-09-05 07:02:06 +02:00
use Friendica\Protocol\DFRN ;
2018-10-21 07:53:47 +02:00
use Friendica\Protocol\Diaspora ;
2017-11-19 22:55:28 +01:00
use Friendica\Protocol\OStatus ;
2017-12-02 15:32:45 +01:00
use Friendica\Protocol\Salmon ;
2018-01-27 03:38:34 +01:00
use Friendica\Util\DateTimeFormat ;
2019-10-18 03:26:15 +02:00
use Friendica\Util\Images ;
2018-01-27 05:51:41 +01:00
use Friendica\Util\Network ;
2018-11-08 14:45:46 +01:00
use Friendica\Util\Strings ;
2017-11-19 22:55:28 +01:00
2017-11-22 15:02:55 +01:00
/**
2020-01-19 07:05:23 +01:00
* functions for interacting with a contact
2017-11-22 15:02:55 +01:00
*/
2019-12-15 23:28:01 +01:00
class Contact
2017-11-19 22:55:28 +01:00
{
2018-07-28 01:25:57 +02:00
/**
2019-01-06 18:37:48 +01:00
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_NORMAL
2018-07-28 01:25:57 +02:00
*/
2019-01-06 18:37:48 +01:00
const PAGE_NORMAL = User :: PAGE_FLAGS_NORMAL ;
/**
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_SOAPBOX
*/
const PAGE_SOAPBOX = User :: PAGE_FLAGS_SOAPBOX ;
/**
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_COMMUNITY
*/
const PAGE_COMMUNITY = User :: PAGE_FLAGS_COMMUNITY ;
/**
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_FREELOVE
*/
const PAGE_FREELOVE = User :: PAGE_FLAGS_FREELOVE ;
/**
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_BLOG
*/
const PAGE_BLOG = User :: PAGE_FLAGS_BLOG ;
/**
* @ deprecated since version 2019.03
* @ see User :: PAGE_FLAGS_PRVGROUP
*/
const PAGE_PRVGROUP = User :: PAGE_FLAGS_PRVGROUP ;
2018-07-28 01:25:57 +02:00
/**
* @ }
*/
/**
2019-01-12 20:52:36 +01:00
* Account types
2018-07-28 01:25:57 +02:00
*
2019-01-06 23:08:35 +01:00
* TYPE_UNKNOWN - the account has been imported from gcontact where this is the default type value
*
* TYPE_PERSON - the account belongs to a person
2018-07-28 01:25:57 +02:00
* Associated page types : PAGE_NORMAL , PAGE_SOAPBOX , PAGE_FREELOVE
*
2019-01-06 23:08:35 +01:00
* TYPE_ORGANISATION - the account belongs to an organisation
2018-07-28 01:25:57 +02:00
* Associated page type : PAGE_SOAPBOX
*
2019-01-06 23:08:35 +01:00
* TYPE_NEWS - the account is a news reflector
2018-07-28 01:25:57 +02:00
* Associated page type : PAGE_SOAPBOX
*
2019-01-06 23:08:35 +01:00
* TYPE_COMMUNITY - the account is community forum
2018-07-28 01:25:57 +02:00
* Associated page types : PAGE_COMMUNITY , PAGE_PRVGROUP
*
2019-01-06 23:08:35 +01:00
* TYPE_RELAY - the account is a relay
2018-07-28 01:25:57 +02:00
* This will only be assigned to contacts , not to user accounts
* @ {
*/
2019-01-06 23:08:35 +01:00
const TYPE_UNKNOWN = - 1 ;
const TYPE_PERSON = User :: ACCOUNT_TYPE_PERSON ;
const TYPE_ORGANISATION = User :: ACCOUNT_TYPE_ORGANISATION ;
const TYPE_NEWS = User :: ACCOUNT_TYPE_NEWS ;
const TYPE_COMMUNITY = User :: ACCOUNT_TYPE_COMMUNITY ;
const TYPE_RELAY = User :: ACCOUNT_TYPE_RELAY ;
2018-07-28 01:25:57 +02:00
/**
* @ }
*/
2018-07-25 04:53:46 +02:00
/**
2019-01-06 22:06:53 +01:00
* Contact_is
2018-07-25 04:53:46 +02:00
*
* Relationship types
* @ {
*/
const FOLLOWER = 1 ;
const SHARING = 2 ;
const FRIEND = 3 ;
/**
* @ }
*/
2019-04-28 04:21:02 +02:00
/**
* @ param array $fields Array of selected fields , empty for all
* @ param array $condition Array of fields for condition
* @ param array $params Array of several parameters
* @ return array
* @ throws \Exception
*/
2019-07-27 17:57:23 +02:00
public static function selectToArray ( array $fields = [], array $condition = [], array $params = [])
2019-04-28 04:21:02 +02:00
{
2019-07-27 17:57:23 +02:00
return DBA :: selectToArray ( 'contact' , $fields , $condition , $params );
2019-04-28 04:21:02 +02:00
}
2019-06-11 03:33:25 +02:00
/**
* @ param array $fields Array of selected fields , empty for all
* @ param array $condition Array of fields for condition
* @ param array $params Array of several parameters
* @ return array
* @ throws \Exception
*/
public static function selectFirst ( array $fields = [], array $condition = [], array $params = [])
{
$contact = DBA :: selectFirst ( 'contact' , $fields , $condition , $params );
return $contact ;
}
2019-08-28 22:27:48 +02:00
/**
2019-08-29 06:07:07 +02:00
* Insert a row into the contact table
* Important : You can ' t use DBA :: lastInsertId () after this call since it will be set to 0.
2019-08-28 22:27:48 +02:00
*
2019-08-30 07:59:18 +02:00
* @ param array $fields field array
2019-08-28 22:27:48 +02:00
* @ param bool $on_duplicate_update Do an update on a duplicate entry
*
* @ return boolean was the insert successful ?
* @ throws \Exception
*/
2019-08-30 07:59:18 +02:00
public static function insert ( array $fields , bool $on_duplicate_update = false )
2019-08-28 22:27:48 +02:00
{
2019-08-30 07:59:18 +02:00
$ret = DBA :: insert ( 'contact' , $fields , $on_duplicate_update );
2019-08-29 07:22:29 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'nurl' , 'uid' ], [ 'id' => DBA :: lastInsertId ()]);
2019-08-28 22:27:48 +02:00
if ( ! DBA :: isResult ( $contact )) {
// Shouldn't happen
return $ret ;
}
// Search for duplicated contacts and get rid of them
2019-08-30 07:52:21 +02:00
self :: removeDuplicates ( $contact [ 'nurl' ], $contact [ 'uid' ]);
2019-08-28 22:27:48 +02:00
return $ret ;
}
2019-03-01 10:50:31 +01:00
/**
2019-05-06 20:46:30 +02:00
* @ param integer $id Contact ID
* @ param array $fields Array of selected fields , empty for all
2019-03-01 10:50:31 +01:00
* @ return array | boolean Contact record if it exists , false otherwise
* @ throws \Exception
*/
2019-05-06 20:46:30 +02:00
public static function getById ( $id , $fields = [])
2019-03-01 10:50:31 +01:00
{
2019-05-06 20:46:30 +02:00
return DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $id ]);
2019-03-01 10:50:31 +01:00
}
2020-06-26 07:28:25 +02:00
/**
* Fetches a contact by a given url
*
* @ param string $url profile url
* @ param integer $uid User ID of the contact
* @ param array $fields Field list
* @ param boolean $update true = always update , false = never update , null = update when not found or outdated
* @ return array contact array
*/
public static function getByURL ( string $url , int $uid = 0 , array $fields = [], $update = null )
{
if ( $update || is_null ( $update )) {
$cid = self :: getIdForURL ( $url , $uid , ! ( $update ? ? false ));
if ( empty ( $cid )) {
return [];
}
return self :: getById ( $cid , $fields );
}
// We first try the nurl (http://server.tld/nick), most common case
$options = [ 'order' => [ 'id' ]];
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $url ), 'uid' => $uid , 'deleted' => false ], $options );
// Then the addr (nick@server.tld)
if ( ! DBA :: isResult ( $contact )) {
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'addr' => str_replace ( 'acct:' , '' , $url ), 'uid' => $uid , 'deleted' => false ], $options );
}
// Then the alias (which could be anything)
if ( ! DBA :: isResult ( $contact )) {
// The link could be provided as http although we stored it as https
$ssl_url = str_replace ( 'http://' , 'https://' , $url );
$condition = [ '`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`' , $url , Strings :: normaliseLink ( $url ), $ssl_url , $uid ];
$contact = DBA :: selectFirst ( 'contact' , $fields , $condition , $options );
}
return $contact ;
}
2018-12-08 21:28:01 +01:00
/**
2020-01-19 07:05:23 +01:00
* Tests if the given contact is a follower
2018-12-08 21:28:01 +01:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return boolean is the contact id a follower ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-12-08 21:28:01 +01:00
*/
public static function isFollower ( $cid , $uid )
{
if ( self :: isBlockedByUser ( $cid , $uid )) {
return false ;
}
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata [ 'user' ])) {
return false ;
}
$condition = [ 'id' => $cdata [ 'user' ], 'rel' => [ self :: FOLLOWER , self :: FRIEND ]];
return DBA :: exists ( 'contact' , $condition );
}
2019-07-17 23:37:13 +02:00
/**
2020-01-19 07:05:23 +01:00
* Tests if the given contact url is a follower
2019-07-17 23:37:13 +02:00
*
* @ param string $url Contact URL
* @ param int $uid User ID
*
* @ return boolean is the contact id a follower ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-07-17 23:37:13 +02:00
* @ throws \ImagickException
*/
public static function isFollowerByURL ( $url , $uid )
{
$cid = self :: getIdForURL ( $url , $uid , true );
if ( empty ( $cid )) {
return false ;
}
return self :: isFollower ( $cid , $uid );
}
/**
2020-01-19 07:05:23 +01:00
* Tests if the given user follow the given contact
2019-07-17 23:37:13 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return boolean is the contact url being followed ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-07-17 23:37:13 +02:00
* @ throws \ImagickException
*/
public static function isSharing ( $cid , $uid )
{
if ( self :: isBlockedByUser ( $cid , $uid )) {
return false ;
}
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata [ 'user' ])) {
return false ;
}
$condition = [ 'id' => $cdata [ 'user' ], 'rel' => [ self :: SHARING , self :: FRIEND ]];
return DBA :: exists ( 'contact' , $condition );
}
/**
2020-01-19 07:05:23 +01:00
* Tests if the given user follow the given contact url
2019-07-17 23:37:13 +02:00
*
* @ param string $url Contact URL
* @ param int $uid User ID
*
* @ return boolean is the contact url being followed ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-07-17 23:37:13 +02:00
* @ throws \ImagickException
*/
public static function isSharingByURL ( $url , $uid )
{
$cid = self :: getIdForURL ( $url , $uid , true );
if ( empty ( $cid )) {
return false ;
}
return self :: isSharing ( $cid , $uid );
}
2018-12-04 08:12:55 +01:00
/**
2020-01-19 07:05:23 +01:00
* Get the basepath for a given contact link
2018-12-04 08:12:55 +01:00
*
* @ param string $url The contact link
2020-01-01 19:57:55 +01:00
* @ param boolean $dont_update Don ' t update the contact
2018-12-04 08:12:55 +01:00
*
* @ return string basepath
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-12-04 08:12:55 +01:00
*/
2019-09-21 15:17:33 +02:00
public static function getBasepath ( $url , $dont_update = false )
2018-12-04 08:12:55 +01:00
{
2020-01-01 18:54:36 +01:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'baseurl' ], [ 'uid' => 0 , 'nurl' => Strings :: normaliseLink ( $url )]);
if ( ! DBA :: isResult ( $contact )) {
return '' ;
}
2019-07-05 08:41:48 +02:00
if ( ! empty ( $contact [ 'baseurl' ])) {
return $contact [ 'baseurl' ];
2019-09-21 15:17:33 +02:00
} elseif ( $dont_update ) {
return '' ;
2018-12-04 08:12:55 +01:00
}
2020-01-01 18:54:36 +01:00
// Update the existing contact
self :: updateFromProbe ( $contact [ 'id' ], '' , true );
2019-07-05 08:41:48 +02:00
2020-01-01 18:54:36 +01:00
// And fetch the result
$contact = DBA :: selectFirst ( 'contact' , [ 'baseurl' ], [ 'id' => $contact [ 'id' ]]);
if ( empty ( $contact [ 'baseurl' ])) {
Logger :: info ( 'No baseurl for contact' , [ 'url' => $url ]);
return '' ;
2019-07-05 08:41:48 +02:00
}
2020-01-01 18:54:36 +01:00
Logger :: info ( 'Found baseurl for contact' , [ 'url' => $url , 'baseurl' => $contact [ 'baseurl' ]]);
return $contact [ 'baseurl' ];
2018-12-04 08:12:55 +01:00
}
2019-09-21 14:39:07 +02:00
/**
2019-09-21 15:19:00 +02:00
* Check if the given contact url is on the same server
2019-09-21 14:39:07 +02:00
*
* @ param string $url The contact link
*
2019-09-21 15:19:00 +02:00
* @ return boolean Is it the same server ?
2019-09-21 14:39:07 +02:00
*/
public static function isLocal ( $url )
{
2019-12-30 23:00:08 +01:00
return Strings :: compareLink ( self :: getBasepath ( $url , true ), DI :: baseUrl ());
2019-09-21 14:39:07 +02:00
}
2020-01-16 07:43:21 +01:00
/**
* Check if the given contact ID is on the same server
*
* @ param string $url The contact link
*
* @ return boolean Is it the same server ?
*/
public static function isLocalById ( int $cid )
{
$contact = DBA :: selectFirst ( 'contact' , [ 'url' , 'baseurl' ], [ 'id' => $cid ]);
if ( ! DBA :: isResult ( $contact )) {
return false ;
}
if ( empty ( $contact [ 'baseurl' ])) {
$baseurl = self :: getBasepath ( $contact [ 'url' ], true );
} else {
$baseurl = $contact [ 'baseurl' ];
}
return Strings :: compareLink ( $baseurl , DI :: baseUrl ());
}
2019-02-10 18:19:10 +01:00
/**
* Returns the public contact id of the given user id
*
* @ param integer $uid User ID
*
* @ return integer | boolean Public contact id for given user id
2020-01-19 16:29:55 +01:00
* @ throws \Exception
2019-02-10 18:19:10 +01:00
*/
public static function getPublicIdByUserId ( $uid )
{
$self = DBA :: selectFirst ( 'contact' , [ 'url' ], [ 'self' => true , 'uid' => $uid ]);
if ( ! DBA :: isResult ( $self )) {
return false ;
}
return self :: getIdForURL ( $self [ 'url' ], 0 , true );
}
2018-08-25 15:48:00 +02:00
/**
2020-01-19 07:05:23 +01:00
* Returns the contact id for the user and the public contact id for a given contact id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return array with public and user ' s contact id
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-08-25 15:48:00 +02:00
*/
2018-12-08 21:28:01 +01:00
public static function getPublicAndUserContacID ( $cid , $uid )
2018-08-25 15:48:00 +02:00
{
if ( empty ( $uid ) || empty ( $cid )) {
return [];
}
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'uid' , 'url' ], [ 'id' => $cid ]);
if ( ! DBA :: isResult ( $contact )) {
return [];
}
// We quit when the user id don't match the user id of the provided contact
if (( $contact [ 'uid' ] != $uid ) && ( $contact [ 'uid' ] != 0 )) {
return [];
}
if ( $contact [ 'uid' ] != 0 ) {
$pcid = Contact :: getIdForURL ( $contact [ 'url' ], 0 , true , [ 'url' => $contact [ 'url' ]]);
if ( empty ( $pcid )) {
return [];
}
$ucid = $contact [ 'id' ];
} else {
$pcid = $contact [ 'id' ];
$ucid = Contact :: getIdForURL ( $contact [ 'url' ], $uid , true );
}
return [ 'public' => $pcid , 'user' => $ucid ];
}
2019-05-02 22:03:27 +02:00
/**
* Returns contact details for a given contact id in combination with a user id
*
* @ param int $cid A contact ID
* @ param int $uid The User ID
* @ param array $fields The selected fields for the contact
*
* @ return array The contact details
*
* @ throws \Exception
*/
public static function getContactForUser ( $cid , $uid , array $fields = [])
{
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $cid , 'uid' => $uid ]);
if ( ! DBA :: isResult ( $contact )) {
return [];
} else {
return $contact ;
}
}
2018-08-25 15:48:00 +02:00
/**
2020-01-19 07:05:23 +01:00
* Block contact id for user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
* @ param boolean $blocked Is the contact blocked or unblocked ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-08-25 15:48:00 +02:00
*/
public static function setBlockedForUser ( $cid , $uid , $blocked )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
if ( $cdata [ 'user' ] != 0 ) {
DBA :: update ( 'contact' , [ 'blocked' => $blocked ], [ 'id' => $cdata [ 'user' ], 'pending' => false ]);
}
DBA :: update ( 'user-contact' , [ 'blocked' => $blocked ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ], true );
}
/**
2020-01-19 07:05:23 +01:00
* Returns " block " state for contact id and user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return boolean is the contact id blocked for the given user ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-08-25 15:48:00 +02:00
*/
public static function isBlockedByUser ( $cid , $uid )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
$public_blocked = false ;
if ( ! empty ( $cdata [ 'public' ])) {
$public_contact = DBA :: selectFirst ( 'user-contact' , [ 'blocked' ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ]);
if ( DBA :: isResult ( $public_contact )) {
$public_blocked = $public_contact [ 'blocked' ];
}
}
$user_blocked = $public_blocked ;
if ( ! empty ( $cdata [ 'user' ])) {
$user_contact = DBA :: selectFirst ( 'contact' , [ 'blocked' ], [ 'id' => $cdata [ 'user' ], 'pending' => false ]);
if ( DBA :: isResult ( $user_contact )) {
$user_blocked = $user_contact [ 'blocked' ];
}
}
if ( $user_blocked != $public_blocked ) {
DBA :: update ( 'user-contact' , [ 'blocked' => $user_blocked ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ], true );
}
return $user_blocked ;
}
/**
2020-01-19 07:05:23 +01:00
* Ignore contact id for user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
* @ param boolean $ignored Is the contact ignored or unignored ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-08-25 15:48:00 +02:00
*/
public static function setIgnoredForUser ( $cid , $uid , $ignored )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
if ( $cdata [ 'user' ] != 0 ) {
DBA :: update ( 'contact' , [ 'readonly' => $ignored ], [ 'id' => $cdata [ 'user' ], 'pending' => false ]);
}
DBA :: update ( 'user-contact' , [ 'ignored' => $ignored ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ], true );
}
/**
2020-01-19 07:05:23 +01:00
* Returns " ignore " state for contact id and user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return boolean is the contact id ignored for the given user ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-08-25 15:48:00 +02:00
*/
public static function isIgnoredByUser ( $cid , $uid )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
$public_ignored = false ;
if ( ! empty ( $cdata [ 'public' ])) {
$public_contact = DBA :: selectFirst ( 'user-contact' , [ 'ignored' ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ]);
if ( DBA :: isResult ( $public_contact )) {
$public_ignored = $public_contact [ 'ignored' ];
}
}
$user_ignored = $public_ignored ;
if ( ! empty ( $cdata [ 'user' ])) {
$user_contact = DBA :: selectFirst ( 'contact' , [ 'readonly' ], [ 'id' => $cdata [ 'user' ], 'pending' => false ]);
if ( DBA :: isResult ( $user_contact )) {
$user_ignored = $user_contact [ 'readonly' ];
}
}
if ( $user_ignored != $public_ignored ) {
DBA :: update ( 'user-contact' , [ 'ignored' => $user_ignored ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ], true );
}
return $user_ignored ;
}
/**
2020-01-19 07:05:23 +01:00
* Set " collapsed " for contact id and user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
* @ param boolean $collapsed are the contact ' s posts collapsed or uncollapsed ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-08-25 15:48:00 +02:00
*/
public static function setCollapsedForUser ( $cid , $uid , $collapsed )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
DBA :: update ( 'user-contact' , [ 'collapsed' => $collapsed ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ], true );
}
/**
2020-01-19 07:05:23 +01:00
* Returns " collapsed " state for contact id and user id
2018-08-25 15:48:00 +02:00
*
* @ param int $cid Either public contact id or user ' s contact id
* @ param int $uid User ID
*
* @ return boolean is the contact id blocked for the given user ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-08-25 15:48:00 +02:00
*/
public static function isCollapsedByUser ( $cid , $uid )
{
$cdata = self :: getPublicAndUserContacID ( $cid , $uid );
if ( empty ( $cdata )) {
return ;
}
$collapsed = false ;
if ( ! empty ( $cdata [ 'public' ])) {
$public_contact = DBA :: selectFirst ( 'user-contact' , [ 'collapsed' ], [ 'cid' => $cdata [ 'public' ], 'uid' => $uid ]);
if ( DBA :: isResult ( $public_contact )) {
$collapsed = $public_contact [ 'collapsed' ];
}
}
return $collapsed ;
}
2017-12-09 19:31:00 +01:00
/**
2020-01-19 07:05:23 +01:00
* Returns a list of contacts belonging in a group
2017-12-09 19:31:00 +01:00
*
* @ param int $gid
* @ return array
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-12-09 19:31:00 +01:00
*/
public static function getByGroupId ( $gid )
{
$return = [];
2018-07-28 01:25:57 +02:00
2017-12-09 19:31:00 +01:00
if ( intval ( $gid )) {
2018-07-20 14:19:26 +02:00
$stmt = DBA :: p ( ' SELECT `group_member` . `contact-id` , `contact` .*
2017-12-09 19:31:00 +01:00
FROM `contact`
INNER JOIN `group_member`
ON `contact` . `id` = `group_member` . `contact-id`
WHERE `gid` = ?
2017-12-15 04:47:58 +01:00
AND `contact` . `uid` = ?
2017-12-09 19:31:00 +01:00
AND NOT `contact` . `self`
2019-01-12 14:28:14 +01:00
AND NOT `contact` . `deleted`
2017-12-09 19:31:00 +01:00
AND NOT `contact` . `blocked`
AND NOT `contact` . `pending`
ORDER BY `contact` . `name` ASC ' ,
$gid ,
local_user ()
);
2018-07-28 01:25:57 +02:00
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $stmt )) {
2018-07-21 04:03:40 +02:00
$return = DBA :: toArray ( $stmt );
2017-12-09 19:31:00 +01:00
}
}
return $return ;
}
2017-12-04 04:27:49 +01:00
/**
* Creates the self - contact for the provided user id
*
* @ param int $uid
* @ return bool Operation success
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2017-12-04 04:27:49 +01:00
*/
public static function createSelfFromUserId ( $uid )
{
// Only create the entry if it doesn't exist yet
2018-07-20 14:19:26 +02:00
if ( DBA :: exists ( 'contact' , [ 'uid' => $uid , 'self' => true ])) {
2017-12-04 04:27:49 +01:00
return true ;
}
2018-07-20 14:19:26 +02:00
$user = DBA :: selectFirst ( 'user' , [ 'uid' , 'username' , 'nickname' ], [ 'uid' => $uid ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $user )) {
2017-12-04 04:27:49 +01:00
return false ;
}
2018-07-20 14:19:26 +02:00
$return = DBA :: insert ( 'contact' , [
2017-12-04 04:27:49 +01:00
'uid' => $user [ 'uid' ],
2018-01-27 03:38:34 +01:00
'created' => DateTimeFormat :: utcNow (),
2017-12-04 04:27:49 +01:00
'self' => 1 ,
'name' => $user [ 'username' ],
'nick' => $user [ 'nickname' ],
2019-12-30 23:00:08 +01:00
'photo' => DI :: baseUrl () . '/photo/profile/' . $user [ 'uid' ] . '.jpg' ,
'thumb' => DI :: baseUrl () . '/photo/avatar/' . $user [ 'uid' ] . '.jpg' ,
'micro' => DI :: baseUrl () . '/photo/micro/' . $user [ 'uid' ] . '.jpg' ,
2017-12-04 04:27:49 +01:00
'blocked' => 0 ,
'pending' => 0 ,
2019-12-30 23:00:08 +01:00
'url' => DI :: baseUrl () . '/profile/' . $user [ 'nickname' ],
'nurl' => Strings :: normaliseLink ( DI :: baseUrl () . '/profile/' . $user [ 'nickname' ]),
'addr' => $user [ 'nickname' ] . '@' . substr ( DI :: baseUrl (), strpos ( DI :: baseUrl (), '://' ) + 3 ),
'request' => DI :: baseUrl () . '/dfrn_request/' . $user [ 'nickname' ],
'notify' => DI :: baseUrl () . '/dfrn_notify/' . $user [ 'nickname' ],
'poll' => DI :: baseUrl () . '/dfrn_poll/' . $user [ 'nickname' ],
'confirm' => DI :: baseUrl () . '/dfrn_confirm/' . $user [ 'nickname' ],
'poco' => DI :: baseUrl () . '/poco/' . $user [ 'nickname' ],
2018-01-27 03:38:34 +01:00
'name-date' => DateTimeFormat :: utcNow (),
'uri-date' => DateTimeFormat :: utcNow (),
'avatar-date' => DateTimeFormat :: utcNow (),
2017-12-04 04:27:49 +01:00
'closeness' => 0
]);
return $return ;
}
2018-03-24 07:15:18 +01:00
/**
* Updates the self - contact for the provided user id
*
2019-01-06 22:06:53 +01:00
* @ param int $uid
2018-03-24 07:15:18 +01:00
* @ param boolean $update_avatar Force the avatar update
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2018-03-24 07:15:18 +01:00
*/
public static function updateSelfFromUserID ( $uid , $update_avatar = false )
{
2020-02-16 11:55:18 +01:00
$fields = [ 'id' , 'name' , 'nick' , 'location' , 'about' , 'keywords' , 'avatar' ,
2019-09-18 07:20:33 +02:00
'xmpp' , 'contact-type' , 'forum' , 'prv' , 'avatar-date' , 'url' , 'nurl' , 'unsearchable' ,
2018-12-22 21:12:32 +01:00
'photo' , 'thumb' , 'micro' , 'addr' , 'request' , 'notify' , 'poll' , 'confirm' , 'poco' ];
2018-07-20 14:19:26 +02:00
$self = DBA :: selectFirst ( 'contact' , $fields , [ 'uid' => $uid , 'self' => true ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $self )) {
2018-03-24 07:15:18 +01:00
return ;
}
2020-02-20 18:24:41 +01:00
$fields = [ 'nickname' , 'page-flags' , 'account-type' ];
2018-07-20 14:19:26 +02:00
$user = DBA :: selectFirst ( 'user' , $fields , [ 'uid' => $uid ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $user )) {
2018-03-24 07:15:18 +01:00
return ;
}
2020-02-09 08:36:19 +01:00
$fields = [ 'name' , 'photo' , 'thumb' , 'about' , 'address' , 'locality' , 'region' ,
2020-01-28 05:00:18 +01:00
'country-name' , 'pub_keywords' , 'xmpp' , 'net-publish' ];
2020-01-23 01:34:15 +01:00
$profile = DBA :: selectFirst ( 'profile' , $fields , [ 'uid' => $uid ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $profile )) {
2018-03-24 07:15:18 +01:00
return ;
}
2019-06-19 19:05:29 +02:00
$file_suffix = 'jpg' ;
2018-03-24 07:15:18 +01:00
$fields = [ 'name' => $profile [ 'name' ], 'nick' => $user [ 'nickname' ],
2018-03-25 10:15:22 +02:00
'avatar-date' => $self [ 'avatar-date' ], 'location' => Profile :: formatLocation ( $profile ),
2018-03-24 07:15:18 +01:00
'about' => $profile [ 'about' ], 'keywords' => $profile [ 'pub_keywords' ],
2020-02-16 11:55:18 +01:00
'contact-type' => $user [ 'account-type' ],
2019-06-19 19:05:29 +02:00
'xmpp' => $profile [ 'xmpp' ]];
2018-03-24 07:15:18 +01:00
2018-12-11 20:03:29 +01:00
$avatar = Photo :: selectFirst ([ 'resource-id' , 'type' ], [ 'uid' => $uid , 'profile' => true ]);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $avatar )) {
2018-03-25 19:26:40 +02:00
if ( $update_avatar ) {
$fields [ 'avatar-date' ] = DateTimeFormat :: utcNow ();
}
2018-03-25 10:15:22 +02:00
2018-03-25 19:26:40 +02:00
// Creating the path to the avatar, beginning with the file suffix
2019-10-18 03:26:15 +02:00
$types = Images :: supportedTypes ();
2018-03-25 19:26:40 +02:00
if ( isset ( $types [ $avatar [ 'type' ]])) {
$file_suffix = $types [ $avatar [ 'type' ]];
}
2018-03-25 10:15:22 +02:00
2018-03-25 19:26:40 +02:00
// We are adding a timestamp value so that other systems won't use cached content
$timestamp = strtotime ( $fields [ 'avatar-date' ]);
2018-03-25 10:15:22 +02:00
2019-12-30 23:00:08 +01:00
$prefix = DI :: baseUrl () . '/photo/' . $avatar [ 'resource-id' ] . '-' ;
2018-03-25 19:26:40 +02:00
$suffix = '.' . $file_suffix . '?ts=' . $timestamp ;
2018-03-25 10:15:22 +02:00
2018-03-25 19:26:40 +02:00
$fields [ 'photo' ] = $prefix . '4' . $suffix ;
$fields [ 'thumb' ] = $prefix . '5' . $suffix ;
$fields [ 'micro' ] = $prefix . '6' . $suffix ;
} else {
// We hadn't found a photo entry, so we use the default avatar
2019-12-30 23:00:08 +01:00
$fields [ 'photo' ] = DI :: baseUrl () . '/images/person-300.jpg' ;
$fields [ 'thumb' ] = DI :: baseUrl () . '/images/person-80.jpg' ;
$fields [ 'micro' ] = DI :: baseUrl () . '/images/person-48.jpg' ;
2018-03-25 19:26:40 +02:00
}
2018-03-24 07:15:18 +01:00
2019-12-30 23:00:08 +01:00
$fields [ 'avatar' ] = DI :: baseUrl () . '/photo/profile/' . $uid . '.' . $file_suffix ;
2019-01-06 18:37:48 +01:00
$fields [ 'forum' ] = $user [ 'page-flags' ] == User :: PAGE_FLAGS_COMMUNITY ;
$fields [ 'prv' ] = $user [ 'page-flags' ] == User :: PAGE_FLAGS_PRVGROUP ;
2020-02-16 16:39:44 +01:00
$fields [ 'unsearchable' ] = ! $profile [ 'net-publish' ];
2018-03-24 07:15:18 +01:00
2018-05-18 14:26:10 +02:00
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
2019-12-30 23:00:08 +01:00
$fields [ 'url' ] = DI :: baseUrl () . '/profile/' . $user [ 'nickname' ];
2018-11-08 17:28:29 +01:00
$fields [ 'nurl' ] = Strings :: normaliseLink ( $fields [ 'url' ]);
2019-12-30 23:00:08 +01:00
$fields [ 'addr' ] = $user [ 'nickname' ] . '@' . substr ( DI :: baseUrl (), strpos ( DI :: baseUrl (), '://' ) + 3 );
$fields [ 'request' ] = DI :: baseUrl () . '/dfrn_request/' . $user [ 'nickname' ];
$fields [ 'notify' ] = DI :: baseUrl () . '/dfrn_notify/' . $user [ 'nickname' ];
$fields [ 'poll' ] = DI :: baseUrl () . '/dfrn_poll/' . $user [ 'nickname' ];
$fields [ 'confirm' ] = DI :: baseUrl () . '/dfrn_confirm/' . $user [ 'nickname' ];
$fields [ 'poco' ] = DI :: baseUrl () . '/poco/' . $user [ 'nickname' ];
2018-05-18 14:26:10 +02:00
2018-03-24 07:15:18 +01:00
$update = false ;
foreach ( $fields as $field => $content ) {
2018-12-22 21:12:32 +01:00
if ( $self [ $field ] != $content ) {
2018-03-24 07:15:18 +01:00
$update = true ;
}
}
if ( $update ) {
2019-04-09 07:15:23 +02:00
if ( $fields [ 'name' ] != $self [ 'name' ]) {
$fields [ 'name-date' ] = DateTimeFormat :: utcNow ();
}
$fields [ 'updated' ] = DateTimeFormat :: utcNow ();
2018-07-20 14:19:26 +02:00
DBA :: update ( 'contact' , $fields , [ 'id' => $self [ 'id' ]]);
2018-03-25 10:20:13 +02:00
// Update the public contact as well
2018-07-20 14:19:26 +02:00
DBA :: update ( 'contact' , $fields , [ 'uid' => 0 , 'nurl' => $self [ 'nurl' ]]);
2018-04-14 10:03:15 +02:00
// Update the profile
2019-12-30 23:00:08 +01:00
$fields = [ 'photo' => DI :: baseUrl () . '/photo/profile/' . $uid . '.' . $file_suffix ,
'thumb' => DI :: baseUrl () . '/photo/avatar/' . $uid . '.' . $file_suffix ];
2020-01-23 01:34:15 +01:00
DBA :: update ( 'profile' , $fields , [ 'uid' => $uid ]);
2018-03-24 07:15:18 +01:00
}
}
2017-11-19 22:55:28 +01:00
/**
2020-01-19 07:05:23 +01:00
* Marks a contact for removal
2017-11-19 22:55:28 +01:00
*
2017-11-22 15:02:55 +01:00
* @ param int $id contact id
2017-11-19 22:55:28 +01:00
* @ return null
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2017-11-19 22:55:28 +01:00
*/
public static function remove ( $id )
{
// We want just to make sure that we don't delete our "self" contact
2018-07-20 14:19:26 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'uid' ], [ 'id' => $id , 'self' => false ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $contact ) || ! intval ( $contact [ 'uid' ])) {
2017-11-19 22:55:28 +01:00
return ;
}
2018-08-12 19:15:47 +02:00
// Archive the contact
2018-10-13 20:13:01 +02:00
DBA :: update ( 'contact' , [ 'archive' => true , 'network' => Protocol :: PHANTOM , 'deleted' => true ], [ 'id' => $id ]);
2017-11-19 22:55:28 +01:00
2018-08-12 19:15:47 +02:00
// Delete it in the background
2019-02-21 20:32:31 +01:00
Worker :: add ( PRIORITY_MEDIUM , 'RemoveContact' , $id );
2017-11-19 22:55:28 +01:00
}
/**
2020-01-19 07:05:23 +01:00
* Sends an unfriend message . Does not remove the contact
2017-11-19 22:55:28 +01:00
*
2018-09-05 07:02:06 +02:00
* @ param array $user User unfriending
* @ param array $contact Contact unfriended
* @ param boolean $dissolve Remove the contact on the remote side
2017-11-23 05:13:12 +01:00
* @ return void
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2017-11-19 22:55:28 +01:00
*/
2018-09-05 07:02:06 +02:00
public static function terminateFriendship ( array $user , array $contact , $dissolve = false )
2017-11-19 22:55:28 +01:00
{
2019-01-13 19:03:13 +01:00
if ( empty ( $contact [ 'network' ])) {
return ;
}
2019-05-02 15:05:31 +02:00
$protocol = $contact [ 'network' ];
if (( $protocol == Protocol :: DFRN ) && ! self :: isLegacyDFRNContact ( $contact )) {
$protocol = Protocol :: ACTIVITYPUB ;
}
if (( $protocol == Protocol :: DFRN ) && $dissolve ) {
2018-09-05 07:02:06 +02:00
DFRN :: deliver ( $user , $contact , 'placeholder' , true );
2019-05-02 15:05:31 +02:00
} elseif ( in_array ( $protocol , [ Protocol :: OSTATUS , Protocol :: DFRN ])) {
2017-11-19 22:55:28 +01:00
// create an unfollow slap
2018-01-15 14:05:12 +01:00
$item = [];
2019-10-25 00:10:20 +02:00
$item [ 'verb' ] = Activity :: O_UNFOLLOW ;
2020-06-16 22:30:25 +02:00
$item [ 'gravity' ] = GRAVITY_ACTIVITY ;
2017-11-19 22:55:28 +01:00
$item [ 'follow' ] = $contact [ " url " ];
2018-08-01 07:29:58 +02:00
$item [ 'body' ] = '' ;
$item [ 'title' ] = '' ;
$item [ 'guid' ] = '' ;
2020-05-05 07:11:59 +02:00
$item [ 'uri-id' ] = 0 ;
2018-08-01 07:29:58 +02:00
$item [ 'attach' ] = '' ;
2017-11-19 22:55:28 +01:00
$slap = OStatus :: salmon ( $item , $user );
2018-03-16 07:43:10 +01:00
if ( ! empty ( $contact [ 'notify' ])) {
2017-12-02 15:32:45 +01:00
Salmon :: slapper ( $user , $contact [ 'notify' ], $slap );
2017-11-19 22:55:28 +01:00
}
2019-05-02 15:05:31 +02:00
} elseif ( $protocol == Protocol :: DIASPORA ) {
2017-11-19 22:55:28 +01:00
Diaspora :: sendUnshare ( $user , $contact );
2019-05-02 15:05:31 +02:00
} elseif ( $protocol == Protocol :: ACTIVITYPUB ) {
2019-01-10 08:24:12 +01:00
ActivityPub\Transmitter :: sendContactUndo ( $contact [ 'url' ], $contact [ 'id' ], $user [ 'uid' ]);
2018-09-16 00:25:58 +02:00
if ( $dissolve ) {
2018-10-03 17:41:51 +02:00
ActivityPub\Transmitter :: sendContactReject ( $contact [ 'url' ], $contact [ 'hub-verify' ], $user [ 'uid' ]);
2018-09-16 00:25:58 +02:00
}
2017-11-19 22:55:28 +01:00
}
}
/**
2020-01-19 07:05:23 +01:00
* Marks a contact for archival after a communication issue delay
2017-11-19 22:55:28 +01:00
*
* Contact has refused to recognise us as a friend . We will start a countdown .
* If they still don ' t recognise us in 32 days , the relationship is over ,
* and we won ' t waste any more time trying to communicate with them .
* This provides for the possibility that their database is temporarily messed
* up or some other transient event and that there ' s a possibility we could recover from it .
*
2017-11-22 15:02:55 +01:00
* @ param array $contact contact to mark for archival
2017-12-17 21:27:50 +01:00
* @ return null
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2017-11-19 22:55:28 +01:00
*/
public static function markForArchival ( array $contact )
{
2018-08-21 17:35:09 +02:00
if ( ! isset ( $contact [ 'url' ]) && ! empty ( $contact [ 'id' ])) {
$fields = [ 'id' , 'url' , 'archive' , 'self' , 'term-date' ];
2019-01-07 19:26:54 +01:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2018-08-21 17:35:09 +02:00
if ( ! DBA :: isResult ( $contact )) {
return ;
}
} elseif ( ! isset ( $contact [ 'url' ])) {
2018-10-30 14:58:45 +01:00
Logger :: log ( 'Empty contact: ' . json_encode ( $contact ) . ' - ' . System :: callstack ( 20 ), Logger :: DEBUG );
2018-08-17 05:19:42 +02:00
}
2019-01-16 22:39:56 +01:00
Logger :: log ( 'Contact ' . $contact [ 'id' ] . ' is marked for archival' , Logger :: DEBUG );
2017-12-05 22:29:33 +01:00
// Contact already archived or "self" contact? => nothing to do
if ( $contact [ 'archive' ] || $contact [ 'self' ]) {
2017-11-19 22:55:28 +01:00
return ;
}
2017-12-04 20:37:36 +01:00
2018-10-21 07:53:47 +02:00
if ( $contact [ 'term-date' ] <= DBA :: NULL_DATETIME ) {
2018-07-20 14:19:26 +02:00
DBA :: update ( 'contact' , [ 'term-date' => DateTimeFormat :: utcNow ()], [ 'id' => $contact [ 'id' ]]);
2018-11-08 17:28:29 +01:00
DBA :: update ( 'contact' , [ 'term-date' => DateTimeFormat :: utcNow ()], [ '`nurl` = ? AND `term-date` <= ? AND NOT `self`' , Strings :: normaliseLink ( $contact [ 'url' ]), DBA :: NULL_DATETIME ]);
2017-11-19 22:55:28 +01:00
} else {
/* @ todo
* We really should send a notification to the owner after 2 - 3 weeks
* so they won ' t be surprised when the contact vanishes and can take
* remedial action if this was a serious mistake or glitch
*/
/// @todo Check for contact vitality via probing
2020-01-19 21:21:13 +01:00
$archival_days = DI :: config () -> get ( 'system' , 'archival_days' , 32 );
2018-04-10 13:10:02 +02:00
$expiry = $contact [ 'term-date' ] . ' + ' . $archival_days . ' days ' ;
2018-01-27 03:38:34 +01:00
if ( DateTimeFormat :: utcNow () > DateTimeFormat :: utc ( $expiry )) {
2017-11-19 22:55:28 +01:00
/* Relationship is really truly dead . archive them rather than
* delete , though if the owner tries to unarchive them we ' ll start
* the whole process over again .
*/
2019-08-27 21:01:11 +02:00
DBA :: update ( 'contact' , [ 'archive' => true ], [ 'id' => $contact [ 'id' ]]);
DBA :: update ( 'contact' , [ 'archive' => true ], [ 'nurl' => Strings :: normaliseLink ( $contact [ 'url' ]), 'self' => false ]);
2019-07-03 07:46:35 +02:00
GContact :: updateFromPublicContactURL ( $contact [ 'url' ]);
2017-11-19 22:55:28 +01:00
}
}
}
/**
2020-01-19 07:05:23 +01:00
* Cancels the archival countdown
2017-11-19 22:55:28 +01:00
*
2019-01-06 22:06:53 +01:00
* @ see Contact :: markForArchival ()
2017-11-19 22:55:28 +01:00
*
2017-11-22 15:02:55 +01:00
* @ param array $contact contact to be unmarked for archival
2017-11-19 22:55:28 +01:00
* @ return null
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-11-19 22:55:28 +01:00
*/
public static function unmarkForArchival ( array $contact )
{
2019-08-29 08:41:55 +02:00
// Always unarchive the relay contact entry
2019-08-31 11:27:19 +02:00
if ( ! empty ( $contact [ 'batch' ]) && ! empty ( $contact [ 'term-date' ]) && ( $contact [ 'term-date' ] > DBA :: NULL_DATETIME )) {
2019-08-29 08:41:55 +02:00
$fields = [ 'term-date' => DBA :: NULL_DATETIME , 'archive' => false ];
2019-09-02 15:11:07 +02:00
$condition = [ 'uid' => 0 , 'network' => Protocol :: FEDERATED , 'batch' => $contact [ 'batch' ], 'contact-type' => self :: TYPE_RELAY ];
2019-08-29 08:41:55 +02:00
DBA :: update ( 'contact' , $fields , $condition );
}
2018-10-21 07:53:47 +02:00
$condition = [ '`id` = ? AND (`term-date` > ? OR `archive`)' , $contact [ 'id' ], DBA :: NULL_DATETIME ];
2018-07-20 14:19:26 +02:00
$exists = DBA :: exists ( 'contact' , $condition );
2017-11-19 22:55:28 +01:00
// We don't need to update, we never marked this contact for archival
2017-11-23 14:15:38 +01:00
if ( ! $exists ) {
2017-11-19 22:55:28 +01:00
return ;
}
2019-01-16 22:39:56 +01:00
Logger :: log ( 'Contact ' . $contact [ 'id' ] . ' is marked as vital again' , Logger :: DEBUG );
2018-08-21 17:35:09 +02:00
if ( ! isset ( $contact [ 'url' ]) && ! empty ( $contact [ 'id' ])) {
$fields = [ 'id' , 'url' , 'batch' ];
2019-01-07 19:26:54 +01:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2018-08-21 17:35:09 +02:00
if ( ! DBA :: isResult ( $contact )) {
return ;
}
}
2017-11-19 22:55:28 +01:00
// It's a miracle. Our dead contact has inexplicably come back to life.
2018-10-21 07:53:47 +02:00
$fields = [ 'term-date' => DBA :: NULL_DATETIME , 'archive' => false ];
2018-07-20 14:19:26 +02:00
DBA :: update ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2019-08-27 21:01:11 +02:00
DBA :: update ( 'contact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $contact [ 'url' ]), 'self' => false ]);
2019-07-03 07:46:35 +02:00
GContact :: updateFromPublicContactURL ( $contact [ 'url' ]);
2017-11-19 22:55:28 +01:00
}
/**
2020-01-19 07:05:23 +01:00
* Get contact data for a given profile link
2017-11-19 22:55:28 +01:00
*
* The function looks at several places ( contact table and gcontact table ) for the contact
* It caches its result for the same script execution to prevent duplicate calls
*
2017-11-22 15:02:55 +01:00
* @ param string $url The profile link
* @ param int $uid User id
* @ param array $default If not data was found take this data as default value
2017-11-19 22:55:28 +01:00
*
* @ return array Contact data
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2017-11-19 22:55:28 +01:00
*/
public static function getDetailsByURL ( $url , $uid = - 1 , array $default = [])
{
2018-01-15 14:05:12 +01:00
static $cache = [];
2017-11-19 22:55:28 +01:00
if ( $url == '' ) {
return $default ;
}
if ( $uid == - 1 ) {
$uid = local_user ();
}
if ( isset ( $cache [ $url ][ $uid ])) {
return $cache [ $url ][ $uid ];
}
$ssl_url = str_replace ( 'http://' , 'https://' , $url );
2019-10-17 03:16:23 +02:00
$nurl = Strings :: normaliseLink ( $url );
2017-11-19 22:55:28 +01:00
// Fetch contact data from the contact table for the given user
2018-07-20 14:19:26 +02:00
$s = DBA :: p ( " SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-02-16 11:55:18 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self` , `rel` , `pending`
2019-10-17 03:16:23 +02:00
FROM `contact` WHERE `nurl` = ? AND `uid` = ? " , $nurl , $uid );
2018-07-21 04:03:40 +02:00
$r = DBA :: toArray ( $s );
2017-11-19 22:55:28 +01:00
// Fetch contact data from the contact table for the given user, checking with the alias
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2018-07-20 14:19:26 +02:00
$s = DBA :: p ( " SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-02-16 11:55:18 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self` , `rel` , `pending`
2019-10-17 03:16:23 +02:00
FROM `contact` WHERE `alias` IN ( ? , ? , ? ) AND `uid` = ? " , $nurl , $url , $ssl_url , $uid );
2018-07-21 04:03:40 +02:00
$r = DBA :: toArray ( $s );
2017-11-19 22:55:28 +01:00
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2018-07-20 14:19:26 +02:00
$s = DBA :: p ( " SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-02-16 11:55:18 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self` , `rel` , `pending`
2019-10-17 03:16:23 +02:00
FROM `contact` WHERE `nurl` = ? AND `uid` = 0 " , $nurl );
2018-07-21 04:03:40 +02:00
$r = DBA :: toArray ( $s );
2017-11-19 22:55:28 +01:00
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2018-07-20 14:19:26 +02:00
$s = DBA :: p ( " SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-02-16 11:55:18 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self` , `rel` , `pending`
2019-10-17 03:16:23 +02:00
FROM `contact` WHERE `alias` IN ( ? , ? , ? ) AND `uid` = 0 " , $nurl , $url , $ssl_url );
2018-07-21 04:03:40 +02:00
$r = DBA :: toArray ( $s );
2017-11-19 22:55:28 +01:00
}
// Fetch the data from the gcontact table
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2018-07-20 14:19:26 +02:00
$s = DBA :: p ( " SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
2020-02-16 11:55:18 +01:00
`keywords` , `photo` , `photo` AS `thumb` , `photo` AS `micro` , 0 AS `forum` , 0 AS `prv` , `community` , `contact-type` , `birthday` , 0 AS `self` , 2 AS `rel` , 0 AS `pending`
2019-10-17 03:16:23 +02:00
FROM `gcontact` WHERE `nurl` = ? " , $nurl );
2018-07-21 04:03:40 +02:00
$r = DBA :: toArray ( $s );
2017-11-19 22:55:28 +01:00
}
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2020-02-29 17:41:33 +01:00
$authoritativeResult = true ;
2017-11-19 22:55:28 +01:00
// If there is more than one entry we filter out the connector networks
if ( count ( $r ) > 1 ) {
2017-11-22 15:02:55 +01:00
foreach ( $r as $id => $result ) {
2019-06-11 03:29:11 +02:00
if ( ! in_array ( $result [ " network " ], Protocol :: NATIVE_SUPPORT )) {
2017-11-19 22:55:28 +01:00
unset ( $r [ $id ]);
}
}
}
$profile = array_shift ( $r );
// "bd" always contains the upcoming birthday of a contact.
// "birthday" might contain the birthday including the year of birth.
2018-11-22 05:53:45 +01:00
if ( $profile [ " birthday " ] > DBA :: NULL_DATE ) {
2017-11-19 22:55:28 +01:00
$bd_timestamp = strtotime ( $profile [ " birthday " ]);
$month = date ( " m " , $bd_timestamp );
$day = date ( " d " , $bd_timestamp );
$current_timestamp = time ();
$current_year = date ( " Y " , $current_timestamp );
$current_month = date ( " m " , $current_timestamp );
$current_day = date ( " d " , $current_timestamp );
$profile [ " bd " ] = $current_year . " - " . $month . " - " . $day ;
$current = $current_year . " - " . $current_month . " - " . $current_day ;
if ( $profile [ " bd " ] < $current ) {
$profile [ " bd " ] = ( ++ $current_year ) . " - " . $month . " - " . $day ;
}
} else {
2018-11-22 05:53:45 +01:00
$profile [ " bd " ] = DBA :: NULL_DATE ;
2017-11-19 22:55:28 +01:00
}
} else {
2020-02-29 17:41:33 +01:00
$authoritativeResult = false ;
2017-11-19 22:55:28 +01:00
$profile = $default ;
}
2018-07-10 14:27:56 +02:00
if ( empty ( $profile [ " photo " ]) && isset ( $default [ " photo " ])) {
2017-11-19 22:55:28 +01:00
$profile [ " photo " ] = $default [ " photo " ];
}
2018-07-10 14:27:56 +02:00
if ( empty ( $profile [ " name " ]) && isset ( $default [ " name " ])) {
2017-11-19 22:55:28 +01:00
$profile [ " name " ] = $default [ " name " ];
}
2018-07-10 14:27:56 +02:00
if ( empty ( $profile [ " network " ]) && isset ( $default [ " network " ])) {
2017-11-19 22:55:28 +01:00
$profile [ " network " ] = $default [ " network " ];
}
2018-07-10 14:27:56 +02:00
if ( empty ( $profile [ " thumb " ]) && isset ( $profile [ " photo " ])) {
2017-11-19 22:55:28 +01:00
$profile [ " thumb " ] = $profile [ " photo " ];
}
2018-07-10 14:27:56 +02:00
if ( empty ( $profile [ " micro " ]) && isset ( $profile [ " thumb " ])) {
2017-11-19 22:55:28 +01:00
$profile [ " micro " ] = $profile [ " thumb " ];
}
2019-10-16 14:35:14 +02:00
if (( empty ( $profile [ " addr " ]) || empty ( $profile [ " name " ])) && ! empty ( $profile [ " gid " ])
2019-07-01 20:00:55 +02:00
&& in_array ( $profile [ " network " ], Protocol :: FEDERATED )
2017-11-22 15:02:55 +01:00
) {
2019-07-03 07:46:35 +02:00
Worker :: add ( PRIORITY_LOW , " UpdateGContact " , $url );
2017-11-19 22:55:28 +01:00
}
// Show contact details of Diaspora contacts only if connected
2019-10-16 14:35:14 +02:00
if ( empty ( $profile [ " cid " ]) && ( $profile [ " network " ] ? ? " " ) == Protocol :: DIASPORA ) {
2017-11-19 22:55:28 +01:00
$profile [ " location " ] = " " ;
$profile [ " about " ] = " " ;
2018-11-22 05:53:45 +01:00
$profile [ " birthday " ] = DBA :: NULL_DATE ;
2017-11-19 22:55:28 +01:00
}
2020-02-29 17:41:33 +01:00
// Only cache the result if it came from the DB since this method is used in widely different contexts
// @see display_fetch_author for an example of $default parameter diverging from the DB result
if ( $authoritativeResult ) {
$cache [ $url ][ $uid ] = $profile ;
}
2017-11-19 22:55:28 +01:00
return $profile ;
}
/**
2020-01-19 07:05:23 +01:00
* Get contact data for a given address
2017-11-19 22:55:28 +01:00
*
* The function looks at several places ( contact table and gcontact table ) for the contact
*
* @ param string $addr The profile link
2017-11-22 15:02:55 +01:00
* @ param int $uid User id
2017-11-19 22:55:28 +01:00
*
* @ return array Contact data
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2017-11-19 22:55:28 +01:00
*/
public static function getDetailsByAddr ( $addr , $uid = - 1 )
{
if ( $addr == '' ) {
2018-01-15 14:05:12 +01:00
return [];
2017-11-19 22:55:28 +01:00
}
if ( $uid == - 1 ) {
$uid = local_user ();
}
// Fetch contact data from the contact table for the given user
$r = q ( " SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-03-25 17:42:53 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self` , `rel` , `pending` , `baseurl`
2018-10-13 20:13:01 +02:00
FROM `contact` WHERE `addr` = '%s' AND `uid` = % d AND NOT `deleted` " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $addr ),
2018-01-11 09:37:11 +01:00
intval ( $uid )
);
2017-11-19 22:55:28 +01:00
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-19 22:55:28 +01:00
$r = q ( " SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
2020-03-25 17:42:53 +01:00
`keywords` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self` , `rel` , `pending` , `baseurl`
2018-10-13 20:13:01 +02:00
FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 AND NOT `deleted` " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $addr )
2018-01-11 09:37:11 +01:00
);
}
2017-11-19 22:55:28 +01:00
// Fetch the data from the gcontact table
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-19 22:55:28 +01:00
$r = q ( " SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
2020-03-25 17:42:53 +01:00
`keywords` , `photo` , `photo` AS `thumb` , `photo` AS `micro` , `community` AS `forum` , 0 AS `prv` , `community` , `contact-type` , `birthday` , 0 AS `self` , 2 AS `rel` , 0 AS `pending` , `server_url` AS `baseurl`
2018-01-11 09:37:11 +01:00
FROM `gcontact` WHERE `addr` = '%s' " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $addr )
2018-01-11 09:37:11 +01:00
);
}
2017-11-19 22:55:28 +01:00
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-19 22:55:28 +01:00
$data = Probe :: uri ( $addr );
2020-06-06 20:36:04 +02:00
$profile = self :: getDetailsByURL ( $data [ 'url' ], $uid , $data );
2017-11-19 22:55:28 +01:00
} else {
$profile = $r [ 0 ];
}
return $profile ;
}
/**
2020-01-19 07:05:23 +01:00
* Returns the data array for the photo menu of a given contact
2017-11-19 22:55:28 +01:00
*
2017-11-22 15:02:55 +01:00
* @ param array $contact contact
* @ param int $uid optional , default 0
2017-11-19 22:55:28 +01:00
* @ return array
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2017-11-19 22:55:28 +01:00
*/
public static function photoMenu ( array $contact , $uid = 0 )
{
$pm_url = '' ;
$status_link = '' ;
$photos_link = '' ;
$contact_drop_link = '' ;
$poke_link = '' ;
if ( $uid == 0 ) {
$uid = local_user ();
}
2018-07-10 14:27:56 +02:00
if ( empty ( $contact [ 'uid' ]) || ( $contact [ 'uid' ] != $uid )) {
2017-11-19 22:55:28 +01:00
if ( $uid == 0 ) {
2018-06-02 10:05:06 +02:00
$profile_link = self :: magicLink ( $contact [ 'url' ]);
2020-01-18 20:52:34 +01:00
$menu = [ 'profile' => [ DI :: l10n () -> t ( 'View Profile' ), $profile_link , true ]];
2017-11-19 22:55:28 +01:00
return $menu ;
}
2018-01-11 09:26:30 +01:00
// Look for our own contact if the uid doesn't match and isn't public
2018-07-20 14:19:26 +02:00
$contact_own = DBA :: selectFirst ( 'contact' , [], [ 'nurl' => $contact [ 'nurl' ], 'network' => $contact [ 'network' ], 'uid' => $uid ]);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $contact_own )) {
2018-01-11 09:26:30 +01:00
return self :: photoMenu ( $contact_own , $uid );
2017-11-19 22:55:28 +01:00
}
}
$sparkle = false ;
2019-09-11 22:03:29 +02:00
if (( $contact [ 'network' ] === Protocol :: DFRN ) && ! $contact [ 'self' ] && empty ( $contact [ 'pending' ])) {
2017-11-19 22:55:28 +01:00
$sparkle = true ;
2019-12-30 23:00:08 +01:00
$profile_link = DI :: baseUrl () . '/redir/' . $contact [ 'id' ];
2017-11-19 22:55:28 +01:00
} else {
$profile_link = $contact [ 'url' ];
}
if ( $profile_link === 'mailbox' ) {
$profile_link = '' ;
}
if ( $sparkle ) {
2020-01-28 01:21:18 +01:00
$status_link = $profile_link . '/status' ;
2019-02-26 10:19:08 +01:00
$photos_link = str_replace ( '/profile/' , '/photos/' , $profile_link );
2020-01-28 01:21:18 +01:00
$profile_link = $profile_link . '/profile' ;
2017-11-19 22:55:28 +01:00
}
2019-09-11 22:03:29 +02:00
if ( self :: canReceivePrivateMessages ( $contact ) && empty ( $contact [ 'pending' ])) {
2019-12-30 23:00:08 +01:00
$pm_url = DI :: baseUrl () . '/message/new/' . $contact [ 'id' ];
2017-11-19 22:55:28 +01:00
}
2019-09-11 22:03:29 +02:00
if (( $contact [ 'network' ] == Protocol :: DFRN ) && ! $contact [ 'self' ] && empty ( $contact [ 'pending' ])) {
2020-04-20 17:42:27 +02:00
$poke_link = 'contact/' . $contact [ 'id' ] . '/poke' ;
2017-11-19 22:55:28 +01:00
}
2019-12-30 23:00:08 +01:00
$contact_url = DI :: baseUrl () . '/contact/' . $contact [ 'id' ];
2017-11-19 22:55:28 +01:00
2019-12-30 23:00:08 +01:00
$posts_link = DI :: baseUrl () . '/contact/' . $contact [ 'id' ] . '/conversations' ;
2018-03-17 07:17:32 +01:00
if ( ! $contact [ 'self' ]) {
2019-12-30 23:00:08 +01:00
$contact_drop_link = DI :: baseUrl () . '/contact/' . $contact [ 'id' ] . '/drop?confirm=1' ;
2018-03-17 07:17:32 +01:00
}
2017-11-19 22:55:28 +01:00
2019-10-17 03:16:23 +02:00
$follow_link = '' ;
$unfollow_link = '' ;
2019-12-16 07:35:29 +01:00
if ( ! $contact [ 'self' ] && in_array ( $contact [ 'network' ], Protocol :: NATIVE_SUPPORT )) {
2019-10-17 03:16:23 +02:00
if ( $contact [ 'uid' ] && in_array ( $contact [ 'rel' ], [ self :: SHARING , self :: FRIEND ])) {
$unfollow_link = 'unfollow?url=' . urlencode ( $contact [ 'url' ]);
} elseif ( ! $contact [ 'pending' ]) {
$follow_link = 'follow?url=' . urlencode ( $contact [ 'url' ]);
}
}
2020-02-17 23:25:12 +01:00
if ( ! empty ( $follow_link ) || ! empty ( $unfollow_link )) {
$contact_drop_link = '' ;
}
2017-11-19 22:55:28 +01:00
/**
2017-11-22 15:02:55 +01:00
* Menu array :
2017-11-19 22:55:28 +01:00
* " name " => [ " Label " , " link " , ( bool ) Should the link opened in a new tab ? ]
*/
2018-08-25 15:48:00 +02:00
if ( empty ( $contact [ 'uid' ])) {
$menu = [
2020-01-18 20:52:34 +01:00
'profile' => [ DI :: l10n () -> t ( 'View Profile' ) , $profile_link , true ],
'network' => [ DI :: l10n () -> t ( 'Network Posts' ) , $posts_link , false ],
'edit' => [ DI :: l10n () -> t ( 'View Contact' ) , $contact_url , false ],
'follow' => [ DI :: l10n () -> t ( 'Connect/Follow' ), $follow_link , true ],
'unfollow' => [ DI :: l10n () -> t ( 'UnFollow' ) , $unfollow_link , true ],
2018-08-25 15:48:00 +02:00
];
} else {
$menu = [
2020-01-18 20:52:34 +01:00
'status' => [ DI :: l10n () -> t ( 'View Status' ) , $status_link , true ],
'profile' => [ DI :: l10n () -> t ( 'View Profile' ) , $profile_link , true ],
'photos' => [ DI :: l10n () -> t ( 'View Photos' ) , $photos_link , true ],
'network' => [ DI :: l10n () -> t ( 'Network Posts' ) , $posts_link , false ],
'edit' => [ DI :: l10n () -> t ( 'View Contact' ) , $contact_url , false ],
'drop' => [ DI :: l10n () -> t ( 'Drop Contact' ) , $contact_drop_link , false ],
'pm' => [ DI :: l10n () -> t ( 'Send PM' ) , $pm_url , false ],
'poke' => [ DI :: l10n () -> t ( 'Poke' ) , $poke_link , false ],
'follow' => [ DI :: l10n () -> t ( 'Connect/Follow' ), $follow_link , true ],
'unfollow' => [ DI :: l10n () -> t ( 'UnFollow' ) , $unfollow_link , true ],
2018-08-25 15:48:00 +02:00
];
2019-09-09 07:29:33 +02:00
2019-09-11 22:03:29 +02:00
if ( ! empty ( $contact [ 'pending' ])) {
2019-09-09 07:29:33 +02:00
$intro = DBA :: selectFirst ( 'intro' , [ 'id' ], [ 'contact-id' => $contact [ 'id' ]]);
if ( DBA :: isResult ( $intro )) {
2020-01-18 20:52:34 +01:00
$menu [ 'follow' ] = [ DI :: l10n () -> t ( 'Approve' ), 'notifications/intros/' . $intro [ 'id' ], true ];
2019-09-09 07:29:33 +02:00
}
}
2018-08-25 15:48:00 +02:00
}
2018-01-11 09:37:11 +01:00
$args = [ 'contact' => $contact , 'menu' => & $menu ];
2017-11-19 22:55:28 +01:00
2018-12-26 07:06:24 +01:00
Hook :: callAll ( 'contact_photo_menu' , $args );
2017-11-19 22:55:28 +01:00
2018-01-15 14:05:12 +01:00
$menucondensed = [];
2017-11-19 22:55:28 +01:00
2017-11-22 15:02:55 +01:00
foreach ( $menu as $menuname => $menuitem ) {
2017-11-19 22:55:28 +01:00
if ( $menuitem [ 1 ] != '' ) {
$menucondensed [ $menuname ] = $menuitem ;
}
}
return $menucondensed ;
}
/**
2020-01-19 07:05:23 +01:00
* Returns ungrouped contact count or list for user
2017-11-22 15:02:55 +01:00
*
2017-11-19 22:55:28 +01:00
* Returns either the total number of ungrouped contacts for the given user
* id or a paginated list of ungrouped contacts .
*
2019-01-06 22:06:53 +01:00
* @ param int $uid uid
2017-11-19 22:55:28 +01:00
* @ return array
2019-01-07 16:24:06 +01:00
* @ throws \Exception
2017-11-19 22:55:28 +01:00
*/
2018-07-10 00:36:50 +02:00
public static function getUngroupedList ( $uid )
2017-11-19 22:55:28 +01:00
{
2018-07-10 00:36:50 +02:00
return q ( " SELECT *
FROM `contact`
WHERE `uid` = % d
AND NOT `self`
2019-01-12 14:28:14 +01:00
AND NOT `deleted`
2018-07-10 00:36:50 +02:00
AND NOT `blocked`
AND NOT `pending`
AND `id` NOT IN (
SELECT DISTINCT ( `contact-id` )
FROM `group_member`
INNER JOIN `group` ON `group` . `id` = `group_member` . `gid`
WHERE `group` . `uid` = % d
) " , intval( $uid ), intval( $uid ));
2017-11-19 22:55:28 +01:00
}
2019-04-08 22:41:18 +02:00
/**
* Have a look at all contact tables for a given profile url .
* This function works as a replacement for probing the contact .
*
2019-04-09 10:47:57 +02:00
* @ param string $url Contact URL
* @ param integer $cid Contact ID
2019-04-08 22:41:18 +02:00
*
* @ return array Contact array in the " probe " structure
*/
2019-04-12 14:50:44 +02:00
private static function getProbeDataFromDatabase ( $url , $cid = null )
2019-04-08 22:41:18 +02:00
{
// The link could be provided as http although we stored it as https
$ssl_url = str_replace ( 'http://' , 'https://' , $url );
2019-05-05 18:40:36 +02:00
$fields = [ 'id' , 'uid' , 'url' , 'addr' , 'alias' , 'notify' , 'poll' , 'name' , 'nick' ,
2019-04-08 22:41:18 +02:00
'photo' , 'keywords' , 'location' , 'about' , 'network' ,
'priority' , 'batch' , 'request' , 'confirm' , 'poco' ];
2019-04-09 10:47:57 +02:00
if ( ! empty ( $cid )) {
$data = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $cid ]);
if ( DBA :: isResult ( $data )) {
return $data ;
}
}
2019-04-08 22:41:18 +02:00
$data = DBA :: selectFirst ( 'contact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $url )]);
2019-04-09 07:15:23 +02:00
if ( ! DBA :: isResult ( $data )) {
2019-04-08 22:41:18 +02:00
$condition = [ 'alias' => [ $url , Strings :: normaliseLink ( $url ), $ssl_url ]];
$data = DBA :: selectFirst ( 'contact' , $fields , $condition );
}
if ( DBA :: isResult ( $data )) {
// For security reasons we don't fetch key data from our users
$data [ " pubkey " ] = '' ;
return $data ;
}
$fields = [ 'url' , 'addr' , 'alias' , 'notify' , 'name' , 'nick' ,
'photo' , 'keywords' , 'location' , 'about' , 'network' ];
$data = DBA :: selectFirst ( 'gcontact' , $fields , [ 'nurl' => Strings :: normaliseLink ( $url )]);
2019-04-09 07:15:23 +02:00
if ( ! DBA :: isResult ( $data )) {
2019-04-08 22:41:18 +02:00
$condition = [ 'alias' => [ $url , Strings :: normaliseLink ( $url ), $ssl_url ]];
$data = DBA :: selectFirst ( 'contact' , $fields , $condition );
}
if ( DBA :: isResult ( $data )) {
$data [ " pubkey " ] = '' ;
$data [ " poll " ] = '' ;
$data [ " priority " ] = 0 ;
$data [ " batch " ] = '' ;
$data [ " request " ] = '' ;
$data [ " confirm " ] = '' ;
$data [ " poco " ] = '' ;
return $data ;
}
$data = ActivityPub :: probeProfile ( $url , false );
if ( ! empty ( $data )) {
return $data ;
}
$fields = [ 'url' , 'addr' , 'alias' , 'notify' , 'poll' , 'name' , 'nick' ,
'photo' , 'network' , 'priority' , 'batch' , 'request' , 'confirm' ];
$data = DBA :: selectFirst ( 'fcontact' , $fields , [ 'url' => $url ]);
2019-04-09 07:15:23 +02:00
if ( ! DBA :: isResult ( $data )) {
2019-04-08 22:41:18 +02:00
$condition = [ 'alias' => [ $url , Strings :: normaliseLink ( $url ), $ssl_url ]];
$data = DBA :: selectFirst ( 'contact' , $fields , $condition );
}
if ( DBA :: isResult ( $data )) {
$data [ " pubkey " ] = '' ;
$data [ " keywords " ] = '' ;
$data [ " location " ] = '' ;
$data [ " about " ] = '' ;
$data [ " poco " ] = '' ;
return $data ;
}
return [];
}
2017-11-19 22:55:28 +01:00
/**
2020-01-19 07:05:23 +01:00
* Fetch the contact id for a given URL and user
2017-11-19 22:55:28 +01:00
*
* First lookup in the contact table to find a record matching either `url` , `nurl` ,
* `addr` or `alias` .
*
* If there 's no record and we aren' t looking for a public contact , we quit .
* If there 's one, we check that it isn' t time to update the picture else we
* directly return the found contact id .
*
2018-01-11 09:37:11 +01:00
* Second , we probe the provided $url whether it ' s http :// server . tld / profile or
2017-11-19 22:55:28 +01:00
* nick @ server . tld . We quit if we can ' t get any info back .
*
* Third , we create the contact record if it doesn ' t exist
*
* Fourth , we update the existing record with the new data ( avatar , alias , nick )
* if there ' s any updates
*
2017-11-22 15:02:55 +01:00
* @ param string $url Contact URL
* @ param integer $uid The user id for the contact ( 0 = public contact )
2017-11-19 22:55:28 +01:00
* @ param boolean $no_update Don ' t update the contact
2018-05-08 19:50:06 +02:00
* @ param array $default Default value for creating the contact when every else fails
2018-10-18 05:33:08 +02:00
* @ param boolean $in_loop Internally used variable to prevent an endless loop
2017-11-19 22:55:28 +01:00
*
* @ return integer Contact ID
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2017-11-19 22:55:28 +01:00
*/
2018-10-18 05:33:08 +02:00
public static function getIdForURL ( $url , $uid = 0 , $no_update = false , $default = [], $in_loop = false )
2017-11-19 22:55:28 +01:00
{
2018-10-30 14:58:45 +01:00
Logger :: log ( " Get contact data for url " . $url . " and user " . $uid . " - " . System :: callstack (), Logger :: DEBUG );
2017-11-19 22:55:28 +01:00
$contact_id = 0 ;
if ( $url == '' ) {
return 0 ;
}
2020-06-26 07:28:25 +02:00
$contact = self :: getByURL ( $url , $uid , [ 'id' , 'avatar' , 'updated' , 'network' ], false );
2017-11-19 22:55:28 +01:00
2020-06-26 07:28:25 +02:00
if ( ! empty ( $contact )) {
2017-11-19 22:55:28 +01:00
$contact_id = $contact [ " id " ];
2019-12-08 22:41:26 +01:00
$update_contact = false ;
2017-11-19 22:55:28 +01:00
2019-12-08 06:19:15 +01:00
// Update the contact every 7 days (Don't update mail or feed contacts)
if ( in_array ( $contact [ 'network' ], Protocol :: FEDERATED )) {
2019-11-25 09:49:14 +01:00
$update_contact = ( $contact [ 'updated' ] < DateTimeFormat :: utc ( 'now -7 days' ));
2017-11-19 22:55:28 +01:00
2019-11-25 09:49:14 +01:00
// We force the update if the avatar is empty
if ( empty ( $contact [ 'avatar' ])) {
$update_contact = true ;
}
2019-12-08 06:19:15 +01:00
} elseif ( empty ( $default ) && in_array ( $contact [ 'network' ], [ Protocol :: MAIL , Protocol :: PHANTOM ]) && ( $uid == 0 )) {
// Update public mail accounts via their user's accounts
$fields = [ 'network' , 'addr' , 'name' , 'nick' , 'avatar' , 'photo' , 'thumb' , 'micro' ];
$mailcontact = DBA :: selectFirst ( 'contact' , $fields , [ " `addr` = ? AND `network` = ? AND `uid` != 0 " , $url , Protocol :: MAIL ]);
if ( ! DBA :: isResult ( $mailcontact )) {
$mailcontact = DBA :: selectFirst ( 'contact' , $fields , [ " `nurl` = ? AND `network` = ? AND `uid` != 0 " , $url , Protocol :: MAIL ]);
}
if ( DBA :: isResult ( $mailcontact )) {
DBA :: update ( 'contact' , $mailcontact , [ 'id' => $contact_id ]);
}
2017-11-19 22:55:28 +01:00
}
2019-04-08 23:05:33 +02:00
2019-04-09 07:15:23 +02:00
// Update the contact in the background if needed but it is called by the frontend
2019-04-09 13:28:45 +02:00
if ( $update_contact && $no_update && in_array ( $contact [ 'network' ], Protocol :: NATIVE_SUPPORT )) {
2019-06-23 11:27:40 +02:00
Worker :: add ( PRIORITY_LOW , " UpdateContact " , $contact_id , ( $uid == 0 ? 'force' : '' ));
2019-04-09 07:15:23 +02:00
}
2017-11-19 22:55:28 +01:00
if ( ! $update_contact || $no_update ) {
return $contact_id ;
}
} elseif ( $uid != 0 ) {
// Non-existing user-specific contact, exiting
return 0 ;
}
2019-04-08 22:41:18 +02:00
if ( $no_update && empty ( $default )) {
2019-07-03 07:46:35 +02:00
// When we don't want to update, we look if we know this contact in any way
2019-04-09 10:47:57 +02:00
$data = self :: getProbeDataFromDatabase ( $url , $contact_id );
2019-04-08 23:05:33 +02:00
$background_update = true ;
2019-07-23 06:26:20 +02:00
} elseif ( $no_update && ! empty ( $default [ 'network' ])) {
2019-07-03 07:46:35 +02:00
// If there are default values, take these
$data = $default ;
$background_update = false ;
2018-08-09 08:19:23 +02:00
} else {
$data = [];
2019-04-08 23:05:33 +02:00
$background_update = false ;
2018-08-09 08:19:23 +02:00
}
if ( empty ( $data )) {
$data = Probe :: uri ( $url , " " , $uid );
}
2017-11-19 22:55:28 +01:00
2019-07-03 07:46:35 +02:00
// Take the default values when probing failed
if ( ! empty ( $default ) && ! in_array ( $data [ " network " ], array_merge ( Protocol :: NATIVE_SUPPORT , [ Protocol :: PUMPIO ]))) {
$data = array_merge ( $data , $default );
2019-04-09 07:15:23 +02:00
}
2019-12-28 20:17:48 +01:00
if ( empty ( $data ) || ( $data [ 'network' ] == Protocol :: PHANTOM )) {
2019-12-30 02:03:00 +01:00
Logger :: info ( 'No valid network found' , [ 'url' => $url , 'data' => $data , 'callstack' => System :: callstack ( 20 )]);
2019-04-09 07:15:23 +02:00
return 0 ;
2017-11-19 22:55:28 +01:00
}
2020-05-24 22:40:00 +02:00
if ( ! empty ( $data [ 'baseurl' ])) {
$data [ 'baseurl' ] = GServer :: cleanURL ( $data [ 'baseurl' ]);
}
if ( ! empty ( $data [ 'baseurl' ]) && empty ( $data [ 'gsid' ])) {
$data [ 'gsid' ] = GServer :: getID ( $data [ 'baseurl' ]);
}
2020-06-25 02:57:47 +02:00
if ( ! $contact_id && ! empty ( $data [ 'alias' ]) && ( $data [ 'alias' ] != $data [ 'url' ]) && ! $in_loop ) {
2018-10-18 05:33:08 +02:00
$contact_id = self :: getIdForURL ( $data [ " alias " ], $uid , true , $default , true );
2017-11-19 22:55:28 +01:00
}
if ( ! $contact_id ) {
2018-12-02 17:25:25 +01:00
$fields = [
2018-01-11 09:37:11 +01:00
'uid' => $uid ,
2018-01-27 03:38:34 +01:00
'created' => DateTimeFormat :: utcNow (),
2019-04-09 13:28:45 +02:00
'url' => $data [ 'url' ],
'nurl' => Strings :: normaliseLink ( $data [ 'url' ]),
2019-10-16 14:35:14 +02:00
'addr' => $data [ 'addr' ] ? ? '' ,
'alias' => $data [ 'alias' ] ? ? '' ,
'notify' => $data [ 'notify' ] ? ? '' ,
'poll' => $data [ 'poll' ] ? ? '' ,
'name' => $data [ 'name' ] ? ? '' ,
'nick' => $data [ 'nick' ] ? ? '' ,
'photo' => $data [ 'photo' ] ? ? '' ,
'keywords' => $data [ 'keywords' ] ? ? '' ,
'location' => $data [ 'location' ] ? ? '' ,
'about' => $data [ 'about' ] ? ? '' ,
2019-04-09 13:28:45 +02:00
'network' => $data [ 'network' ],
2019-10-16 14:35:14 +02:00
'pubkey' => $data [ 'pubkey' ] ? ? '' ,
2018-07-25 04:53:46 +02:00
'rel' => self :: SHARING ,
2019-10-16 14:35:14 +02:00
'priority' => $data [ 'priority' ] ? ? 0 ,
'batch' => $data [ 'batch' ] ? ? '' ,
'request' => $data [ 'request' ] ? ? '' ,
'confirm' => $data [ 'confirm' ] ? ? '' ,
'poco' => $data [ 'poco' ] ? ? '' ,
'baseurl' => $data [ 'baseurl' ] ? ? '' ,
2020-05-22 06:19:32 +02:00
'gsid' => $data [ 'gsid' ] ? ? null ,
2018-01-27 03:38:34 +01:00
'name-date' => DateTimeFormat :: utcNow (),
'uri-date' => DateTimeFormat :: utcNow (),
'avatar-date' => DateTimeFormat :: utcNow (),
2018-01-11 09:37:11 +01:00
'writable' => 1 ,
'blocked' => 0 ,
'readonly' => 0 ,
2018-12-02 17:25:25 +01:00
'pending' => 0 ];
$condition = [ 'nurl' => Strings :: normaliseLink ( $data [ " url " ]), 'uid' => $uid , 'deleted' => false ];
2019-07-14 12:22:19 +02:00
// Before inserting we do check if the entry does exist now.
2019-07-12 23:07:47 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' ], $condition , [ 'order' => [ 'id' ]]);
if ( ! DBA :: isResult ( $contact )) {
2019-07-14 12:22:19 +02:00
Logger :: info ( 'Create new contact' , $fields );
2019-08-29 06:07:07 +02:00
self :: insert ( $fields );
2019-07-14 12:22:19 +02:00
// We intentionally aren't using lastInsertId here. There is a chance for duplicates.
$contact = DBA :: selectFirst ( 'contact' , [ 'id' ], $condition , [ 'order' => [ 'id' ]]);
if ( ! DBA :: isResult ( $contact )) {
Logger :: info ( 'Contact creation failed' , $fields );
// Shouldn't happen
return 0 ;
}
} else {
Logger :: info ( 'Contact had been created before' , [ 'id' => $contact [ " id " ], 'url' => $url , 'contact' => $fields ]);
2017-11-19 22:55:28 +01:00
}
2019-07-12 23:07:47 +02:00
$contact_id = $contact [ " id " ];
2017-11-19 22:55:28 +01:00
}
2019-06-28 06:26:49 +02:00
if ( ! empty ( $data [ 'photo' ]) && ( $data [ 'network' ] != Protocol :: FEED )) {
2019-04-09 13:44:35 +02:00
self :: updateAvatar ( $data [ 'photo' ], $uid , $contact_id );
}
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
if ( in_array ( $data [ " network " ], array_merge ( Protocol :: NATIVE_SUPPORT , [ Protocol :: PUMPIO ]))) {
if ( $background_update ) {
// Update in the background when we fetched the data solely from the database
Worker :: add ( PRIORITY_MEDIUM , " UpdateContact " , $contact_id , ( $uid == 0 ? 'force' : '' ));
} else {
// Else do a direct update
self :: updateFromProbe ( $contact_id , '' , false );
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
// Update the gcontact entry
if ( $uid == 0 ) {
GContact :: updateFromPublicContactID ( $contact_id );
2020-03-10 22:10:59 +01:00
if (( $data [ 'network' ] == Protocol :: ACTIVITYPUB ) && in_array ( DI :: config () -> get ( 'system' , 'gcontact_discovery' ), [ GContact :: DISCOVERY_DIRECT , GContact :: DISCOVERY_RECURSIVE ])) {
GContact :: discoverFollowers ( $data [ 'url' ]);
}
2019-07-12 23:07:47 +02:00
}
}
} else {
2020-05-24 22:40:00 +02:00
$fields = [ 'url' , 'nurl' , 'addr' , 'alias' , 'name' , 'nick' , 'keywords' , 'location' , 'about' , 'avatar-date' , 'baseurl' , 'gsid' ];
2019-07-12 23:07:47 +02:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $contact_id ]);
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
// This condition should always be true
if ( ! DBA :: isResult ( $contact )) {
return $contact_id ;
}
2018-06-03 10:36:05 +02:00
2019-07-12 23:07:47 +02:00
$updated = [
'url' => $data [ 'url' ],
'nurl' => Strings :: normaliseLink ( $data [ 'url' ]),
'updated' => DateTimeFormat :: utcNow ()
];
2018-06-03 10:36:05 +02:00
2020-05-24 22:40:00 +02:00
$fields = [ 'addr' , 'alias' , 'name' , 'nick' , 'keywords' , 'location' , 'about' , 'baseurl' , 'gsid' ];
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
foreach ( $fields as $field ) {
2019-10-16 14:35:14 +02:00
$updated [ $field ] = ( $data [ $field ] ? ? '' ) ? : $contact [ $field ];
2019-07-12 23:07:47 +02:00
}
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
if (( $updated [ 'addr' ] != $contact [ 'addr' ]) || ( ! empty ( $data [ 'alias' ]) && ( $data [ 'alias' ] != $contact [ 'alias' ]))) {
$updated [ 'uri-date' ] = DateTimeFormat :: utcNow ();
}
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
if (( $data [ 'name' ] != $contact [ 'name' ]) || ( $data [ 'nick' ] != $contact [ 'nick' ])) {
$updated [ 'name-date' ] = DateTimeFormat :: utcNow ();
}
2017-11-19 22:55:28 +01:00
2019-07-12 23:07:47 +02:00
DBA :: update ( 'contact' , $updated , [ 'id' => $contact_id ], $contact );
2019-07-02 11:06:48 +02:00
}
2017-11-19 22:55:28 +01:00
return $contact_id ;
}
2019-08-27 21:01:11 +02:00
/**
2020-01-19 07:05:23 +01:00
* Checks if the contact is archived
2019-08-27 21:01:11 +02:00
*
* @ param int $cid contact id
*
* @ return boolean Is the contact archived ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-08-27 21:01:11 +02:00
*/
2019-08-28 16:02:19 +02:00
public static function isArchived ( int $cid )
2019-08-27 21:01:11 +02:00
{
if ( $cid == 0 ) {
return false ;
}
2019-08-28 16:54:49 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'archive' , 'url' , 'batch' ], [ 'id' => $cid ]);
if ( ! DBA :: isResult ( $contact )) {
2019-08-27 21:01:11 +02:00
return false ;
}
2019-08-28 16:54:49 +02:00
if ( $contact [ 'archive' ]) {
2019-08-27 21:01:11 +02:00
return true ;
}
2019-08-28 16:54:49 +02:00
// Check status of ActivityPub endpoints
$apcontact = APContact :: getByURL ( $contact [ 'url' ], false );
if ( ! empty ( $apcontact )) {
if ( ! empty ( $apcontact [ 'inbox' ]) && DBA :: exists ( 'inbox-status' , [ 'archive' => true , 'url' => $apcontact [ 'inbox' ]])) {
return true ;
}
2019-08-27 21:01:11 +02:00
2019-08-28 16:54:49 +02:00
if ( ! empty ( $apcontact [ 'sharedinbox' ]) && DBA :: exists ( 'inbox-status' , [ 'archive' => true , 'url' => $apcontact [ 'sharedinbox' ]])) {
return true ;
}
2019-08-27 21:01:11 +02:00
}
2019-08-28 16:54:49 +02:00
// Check status of Diaspora endpoints
if ( ! empty ( $contact [ 'batch' ])) {
2019-09-02 15:11:07 +02:00
$condition = [ 'archive' => true , 'uid' => 0 , 'network' => Protocol :: FEDERATED , 'batch' => $contact [ 'batch' ], 'contact-type' => self :: TYPE_RELAY ];
return DBA :: exists ( 'contact' , $condition );
2019-08-28 16:02:19 +02:00
}
2019-08-27 21:01:11 +02:00
return false ;
}
2017-11-19 22:55:28 +01:00
/**
2020-01-19 07:05:23 +01:00
* Checks if the contact is blocked
2017-11-19 22:55:28 +01:00
*
* @ param int $cid contact id
*
* @ return boolean Is the contact blocked ?
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2017-11-19 22:55:28 +01:00
*/
public static function isBlocked ( $cid )
{
if ( $cid == 0 ) {
return false ;
}
2018-11-22 22:43:16 +01:00
$blocked = DBA :: selectFirst ( 'contact' , [ 'blocked' , 'url' ], [ 'id' => $cid ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $blocked )) {
2017-11-19 22:55:28 +01:00
return false ;
}
2018-11-22 22:43:16 +01:00
if ( Network :: isUrlBlocked ( $blocked [ 'url' ])) {
return true ;
}
2017-11-19 22:55:28 +01:00
return ( bool ) $blocked [ 'blocked' ];
}
/**
2020-01-19 07:05:23 +01:00
* Checks if the contact is hidden
2017-11-19 22:55:28 +01:00
*
* @ param int $cid contact id
*
* @ return boolean Is the contact hidden ?
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-11-19 22:55:28 +01:00
*/
public static function isHidden ( $cid )
{
if ( $cid == 0 ) {
return false ;
}
2018-07-20 14:19:26 +02:00
$hidden = DBA :: selectFirst ( 'contact' , [ 'hidden' ], [ 'id' => $cid ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $hidden )) {
2017-11-19 22:55:28 +01:00
return false ;
}
return ( bool ) $hidden [ 'hidden' ];
}
/**
2020-01-19 07:05:23 +01:00
* Returns posts from a given contact url
2017-11-19 22:55:28 +01:00
*
* @ param string $contact_url Contact URL
2019-01-06 22:06:53 +01:00
* @ param bool $thread_mode
* @ param int $update
2017-11-19 22:55:28 +01:00
* @ return string posts in HTML
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-11-19 22:55:28 +01:00
*/
2018-08-25 15:48:00 +02:00
public static function getPostsFromUrl ( $contact_url , $thread_mode = false , $update = 0 )
2017-11-19 22:55:28 +01:00
{
2020-06-10 00:38:06 +02:00
return self :: getPostsFromId ( self :: getIdForURL ( $contact_url ), $thread_mode , $update );
}
2017-11-24 23:15:35 +01:00
2020-06-10 00:38:06 +02:00
/**
* Returns posts from a given contact id
*
* @ param integer $cid
* @ param bool $thread_mode
* @ param integer $update
* @ return string posts in HTML
* @ throws \Exception
*/
public static function getPostsFromId ( $cid , $thread_mode = false , $update = 0 )
{
$a = DI :: app ();
2017-11-19 22:55:28 +01:00
2018-09-16 11:06:09 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'contact-type' , 'network' ], [ 'id' => $cid ]);
if ( ! DBA :: isResult ( $contact )) {
2017-11-19 22:55:28 +01:00
return '' ;
}
2019-07-02 00:14:34 +02:00
if ( empty ( $contact [ " network " ]) || in_array ( $contact [ " network " ], Protocol :: FEDERATED )) {
2018-06-10 12:14:53 +02:00
$sql = " (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) " ;
2017-11-19 22:55:28 +01:00
} else {
2018-06-10 12:14:53 +02:00
$sql = " `item`.`uid` = ? " ;
2017-11-19 22:55:28 +01:00
}
2019-12-08 06:19:15 +01:00
$contact_field = ((( $contact [ " contact-type " ] == self :: TYPE_COMMUNITY ) || ( $contact [ 'network' ] == Protocol :: MAIL )) ? 'owner-id' : 'author-id' );
2017-11-19 22:55:28 +01:00
2018-08-25 15:48:00 +02:00
if ( $thread_mode ) {
2018-09-16 11:06:09 +02:00
$condition = [ " ` $contact_field ` = ? AND `gravity` = ? AND " . $sql ,
$cid , GRAVITY_PARENT , local_user ()];
2018-08-25 15:48:00 +02:00
} else {
2018-09-16 11:06:09 +02:00
$condition = [ " ` $contact_field ` = ? AND `gravity` IN (?, ?) AND " . $sql ,
$cid , GRAVITY_PARENT , GRAVITY_COMMENT , local_user ()];
2018-08-25 15:48:00 +02:00
}
2020-02-16 19:04:26 +01:00
if ( DI :: mode () -> isMobile ()) {
$itemsPerPage = DI :: pConfig () -> get ( local_user (), 'system' , 'itemspage_mobile_network' ,
DI :: config () -> get ( 'system' , 'itemspage_network_mobile' ));
} else {
$itemsPerPage = DI :: pConfig () -> get ( local_user (), 'system' , 'itemspage_network' ,
DI :: config () -> get ( 'system' , 'itemspage_network' ));
}
$pager = new Pager ( DI :: l10n (), DI :: args () -> getQueryString (), $itemsPerPage );
2018-10-24 08:15:24 +02:00
2019-07-07 23:30:33 +02:00
$params = [ 'order' => [ 'received' => true ],
2018-10-24 08:15:24 +02:00
'limit' => [ $pager -> getStart (), $pager -> getItemsPerPage ()]];
2017-11-19 22:55:28 +01:00
2018-08-25 15:48:00 +02:00
if ( $thread_mode ) {
$r = Item :: selectThreadForUser ( local_user (), [ 'uri' ], $condition , $params );
$items = Item :: inArray ( $r );
2020-02-14 05:40:00 +01:00
$o = conversation ( $a , $items , 'contacts' , $update , false , 'commented' , local_user ());
2018-08-25 15:48:00 +02:00
} else {
$r = Item :: selectForUser ( local_user (), [], $condition , $params );
2018-06-17 08:27:52 +02:00
2018-08-25 15:48:00 +02:00
$items = Item :: inArray ( $r );
2017-11-19 22:55:28 +01:00
2020-02-14 05:40:00 +01:00
$o = conversation ( $a , $items , 'contact-posts' , false );
2018-08-25 15:48:00 +02:00
}
if ( ! $update ) {
2018-10-24 08:15:24 +02:00
$o .= $pager -> renderMinimal ( count ( $items ));
2018-08-25 15:48:00 +02:00
}
2017-11-19 22:55:28 +01:00
return $o ;
}
/**
2020-01-19 07:05:23 +01:00
* Returns the account type name
2017-11-19 22:55:28 +01:00
*
* The function can be called with either the user or the contact array
*
* @ param array $contact contact or user array
* @ return string
*/
public static function getAccountType ( array $contact )
{
// There are several fields that indicate that the contact or user is a forum
// "page-flags" is a field in the user table,
2019-01-06 18:37:48 +01:00
// "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP.
// "community" is used in the gcontact table and is true if the contact is User::PAGE_FLAGS_COMMUNITY or User::PAGE_FLAGS_PRVGROUP.
if (( isset ( $contact [ 'page-flags' ]) && ( intval ( $contact [ 'page-flags' ]) == User :: PAGE_FLAGS_COMMUNITY ))
|| ( isset ( $contact [ 'page-flags' ]) && ( intval ( $contact [ 'page-flags' ]) == User :: PAGE_FLAGS_PRVGROUP ))
2017-11-19 22:55:28 +01:00
|| ( isset ( $contact [ 'forum' ]) && intval ( $contact [ 'forum' ]))
|| ( isset ( $contact [ 'prv' ]) && intval ( $contact [ 'prv' ]))
|| ( isset ( $contact [ 'community' ]) && intval ( $contact [ 'community' ]))
) {
2019-01-06 23:08:35 +01:00
$type = self :: TYPE_COMMUNITY ;
2017-11-19 22:55:28 +01:00
} else {
2019-01-06 23:08:35 +01:00
$type = self :: TYPE_PERSON ;
2017-11-19 22:55:28 +01:00
}
// The "contact-type" (contact table) and "account-type" (user table) are more general then the chaos from above.
if ( isset ( $contact [ " contact-type " ])) {
$type = $contact [ " contact-type " ];
}
2018-01-11 09:37:11 +01:00
2017-11-19 22:55:28 +01:00
if ( isset ( $contact [ " account-type " ])) {
$type = $contact [ " account-type " ];
}
switch ( $type ) {
2019-01-06 23:08:35 +01:00
case self :: TYPE_ORGANISATION :
2020-01-18 20:52:34 +01:00
$account_type = DI :: l10n () -> t ( " Organisation " );
2017-11-19 22:55:28 +01:00
break ;
2018-07-28 01:25:57 +02:00
2019-01-06 23:08:35 +01:00
case self :: TYPE_NEWS :
2020-01-18 20:52:34 +01:00
$account_type = DI :: l10n () -> t ( 'News' );
2017-11-19 22:55:28 +01:00
break ;
2018-07-28 01:25:57 +02:00
2019-01-06 23:08:35 +01:00
case self :: TYPE_COMMUNITY :
2020-01-18 20:52:34 +01:00
$account_type = DI :: l10n () -> t ( " Forum " );
2017-11-19 22:55:28 +01:00
break ;
2018-07-28 01:25:57 +02:00
2017-11-19 22:55:28 +01:00
default :
$account_type = " " ;
break ;
}
return $account_type ;
}
2017-11-29 23:29:11 +01:00
/**
2020-01-19 07:05:23 +01:00
* Blocks a contact
2017-12-01 06:41:26 +01:00
*
2019-05-16 01:28:00 +02:00
* @ param int $cid
2017-12-01 06:41:26 +01:00
* @ return bool
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-12-01 06:41:26 +01:00
*/
2019-05-16 01:28:00 +02:00
public static function block ( $cid , $reason = null )
2017-12-01 06:41:26 +01:00
{
2019-05-16 01:28:00 +02:00
$return = DBA :: update ( 'contact' , [ 'blocked' => true , 'block_reason' => $reason ], [ 'id' => $cid ]);
2017-12-01 06:41:26 +01:00
return $return ;
}
/**
2020-01-19 07:05:23 +01:00
* Unblocks a contact
2017-12-01 06:41:26 +01:00
*
2019-05-16 01:28:00 +02:00
* @ param int $cid
2017-12-01 06:41:26 +01:00
* @ return bool
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2017-12-01 06:41:26 +01:00
*/
2019-05-16 01:28:00 +02:00
public static function unblock ( $cid )
2017-12-01 06:41:26 +01:00
{
2019-05-16 01:28:00 +02:00
$return = DBA :: update ( 'contact' , [ 'blocked' => false , 'block_reason' => null ], [ 'id' => $cid ]);
2017-12-01 06:41:26 +01:00
return $return ;
2018-01-11 09:37:11 +01:00
}
2017-12-01 06:48:13 +01:00
2018-01-11 09:37:11 +01:00
/**
2020-01-19 07:05:23 +01:00
* Updates the avatar links in a contact only if needed
2017-11-29 23:29:11 +01:00
*
* @ param string $avatar Link to avatar picture
* @ param int $uid User id of contact owner
* @ param int $cid Contact id
* @ param bool $force force picture update
*
2020-03-09 16:39:48 +01:00
* @ return void
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2020-03-09 16:12:33 +01:00
* @ throws HTTPException\NotFoundException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2017-11-29 23:29:11 +01:00
*/
public static function updateAvatar ( $avatar , $uid , $cid , $force = false )
{
2019-06-19 19:05:29 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'avatar' , 'photo' , 'thumb' , 'micro' , 'nurl' ], [ 'id' => $cid , 'self' => false ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $contact )) {
2020-03-09 16:39:48 +01:00
return ;
2017-11-29 23:29:11 +01:00
}
2020-03-09 16:12:33 +01:00
$data = [
$contact [ 'photo' ] ? ? '' ,
$contact [ 'thumb' ] ? ? '' ,
$contact [ 'micro' ] ? ? '' ,
];
2020-01-09 21:41:52 +01:00
foreach ( $data as $image_uri ) {
$image_rid = Photo :: ridFromURI ( $image_uri );
if ( $image_rid && ! Photo :: exists ([ 'resource-id' => $image_rid , 'uid' => $uid ])) {
2020-01-11 16:00:02 +01:00
Logger :: info ( 'Regenerating avatar' , [ 'contact uid' => $uid , 'cid' => $cid , 'missing photo' => $image_rid , 'avatar' => $contact [ 'avatar' ]]);
2020-01-09 21:41:52 +01:00
$force = true ;
}
}
2018-01-11 09:26:30 +01:00
if (( $contact [ " avatar " ] != $avatar ) || $force ) {
2017-12-08 07:15:06 +01:00
$photos = Photo :: importProfilePhoto ( $avatar , $uid , $cid , true );
2017-11-29 23:29:11 +01:00
if ( $photos ) {
2019-06-20 22:09:33 +02:00
$fields = [ 'avatar' => $avatar , 'photo' => $photos [ 0 ], 'thumb' => $photos [ 1 ], 'micro' => $photos [ 2 ], 'avatar-date' => DateTimeFormat :: utcNow ()];
DBA :: update ( 'contact' , $fields , [ 'id' => $cid ]);
2017-11-29 23:29:11 +01:00
// Update the public contact (contact id = 0)
if ( $uid != 0 ) {
2018-07-20 14:19:26 +02:00
$pcontact = DBA :: selectFirst ( 'contact' , [ 'id' ], [ 'nurl' => $contact [ 'nurl' ], 'uid' => 0 ]);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $pcontact )) {
2019-06-20 22:09:33 +02:00
DBA :: update ( 'contact' , $fields , [ 'id' => $pcontact [ 'id' ]]);
2017-11-29 23:29:11 +01:00
}
}
}
}
}
2018-01-09 15:40:45 +01:00
2020-01-19 10:46:31 +01:00
/**
2020-01-19 07:05:23 +01:00
* Helper function for " updateFromProbe " . Updates personal and public contact
2019-06-24 05:25:01 +02:00
*
2019-08-28 01:22:09 +02:00
* @ param integer $id contact id
* @ param integer $uid user id
* @ param string $url The profile URL of the contact
* @ param array $fields The fields that are updated
*
2019-06-24 05:25:01 +02:00
* @ throws \Exception
*/
private static function updateContact ( $id , $uid , $url , array $fields )
{
2019-08-28 01:22:09 +02:00
if ( ! DBA :: update ( 'contact' , $fields , [ 'id' => $id ])) {
Logger :: info ( 'Couldn\'t update contact.' , [ 'id' => $id , 'fields' => $fields ]);
return ;
}
2019-06-24 05:25:01 +02:00
2019-07-12 16:55:23 +02:00
// Search for duplicated contacts and get rid of them
2019-08-30 07:52:21 +02:00
if ( self :: removeDuplicates ( Strings :: normaliseLink ( $url ), $uid ) || ( $uid != 0 )) {
2019-06-24 05:25:01 +02:00
return ;
}
2019-07-02 11:06:48 +02:00
// Update the corresponding gcontact entry
2019-07-03 07:46:35 +02:00
GContact :: updateFromPublicContactID ( $id );
2019-07-02 11:06:48 +02:00
2019-06-27 07:03:58 +02:00
// Archive or unarchive the contact. We only need to do this for the public contact.
// The archive/unarchive function will update the personal contacts by themselves.
$contact = DBA :: selectFirst ( 'contact' , [], [ 'id' => $id ]);
2019-08-28 01:22:09 +02:00
if ( ! DBA :: isResult ( $contact )) {
Logger :: info ( 'Couldn\'t select contact for archival.' , [ 'id' => $id ]);
return ;
}
2019-06-27 07:03:58 +02:00
if ( ! empty ( $fields [ 'success_update' ])) {
self :: unmarkForArchival ( $contact );
} elseif ( ! empty ( $fields [ 'failure_update' ])) {
self :: markForArchival ( $contact );
}
2019-07-01 20:00:55 +02:00
$condition = [ 'self' => false , 'nurl' => Strings :: normaliseLink ( $url ), 'network' => Protocol :: FEDERATED ];
2019-06-24 05:25:01 +02:00
// These contacts are sharing with us, we don't poll them.
// This means that we don't set the update fields in "OnePoll.php".
$condition [ 'rel' ] = self :: SHARING ;
DBA :: update ( 'contact' , $fields , $condition );
unset ( $fields [ 'last-update' ]);
unset ( $fields [ 'success_update' ]);
unset ( $fields [ 'failure_update' ]);
if ( empty ( $fields )) {
return ;
}
// We are polling these contacts, so we mustn't set the update fields here.
$condition [ 'rel' ] = [ self :: FOLLOWER , self :: FRIEND ];
DBA :: update ( 'contact' , $fields , $condition );
}
2020-01-19 10:46:31 +01:00
/**
2020-01-19 07:05:23 +01:00
* Remove duplicated contacts
2019-07-12 16:55:23 +02:00
*
* @ param string $nurl Normalised contact url
* @ param integer $uid User id
2019-07-13 09:25:01 +02:00
* @ return boolean
2019-07-12 16:55:23 +02:00
* @ throws \Exception
*/
2019-08-30 07:52:21 +02:00
public static function removeDuplicates ( string $nurl , int $uid )
2019-07-12 16:55:23 +02:00
{
2019-08-26 17:51:56 +02:00
$condition = [ 'nurl' => $nurl , 'uid' => $uid , 'deleted' => false , 'network' => Protocol :: FEDERATED ];
2019-07-12 16:55:23 +02:00
$count = DBA :: count ( 'contact' , $condition );
if ( $count <= 1 ) {
return false ;
}
2019-08-26 17:51:56 +02:00
$first_contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'network' ], $condition , [ 'order' => [ 'id' ]]);
2019-07-12 16:55:23 +02:00
if ( ! DBA :: isResult ( $first_contact )) {
// Shouldn't happen - so we handle it
return false ;
}
$first = $first_contact [ 'id' ];
2019-08-29 07:22:29 +02:00
Logger :: info ( 'Found duplicates' , [ 'count' => $count , 'first' => $first , 'uid' => $uid , 'nurl' => $nurl ]);
2019-08-26 17:51:56 +02:00
if (( $uid != 0 && ( $first_contact [ 'network' ] == Protocol :: DFRN ))) {
// Don't handle non public DFRN duplicates by now (legacy DFRN is very special because of the key handling)
Logger :: info ( 'Not handling non public DFRN duplicate' , [ 'uid' => $uid , 'nurl' => $nurl ]);
2019-07-12 16:55:23 +02:00
return false ;
}
// Find all duplicates
$condition = [ " `nurl` = ? AND `uid` = ? AND `id` != ? AND NOT `self` AND NOT `deleted` " , $nurl , $uid , $first ];
2019-08-26 17:51:56 +02:00
$duplicates = DBA :: select ( 'contact' , [ 'id' , 'network' ], $condition );
2019-07-12 16:55:23 +02:00
while ( $duplicate = DBA :: fetch ( $duplicates )) {
2019-08-26 17:51:56 +02:00
if ( ! in_array ( $duplicate [ 'network' ], Protocol :: FEDERATED )) {
continue ;
}
2019-07-12 16:55:23 +02:00
2019-08-26 17:51:56 +02:00
Worker :: add ( PRIORITY_HIGH , 'MergeContact' , $first , $duplicate [ 'id' ], $uid );
2019-07-12 16:55:23 +02:00
}
2020-04-28 09:10:18 +02:00
DBA :: close ( $duplicates );
2019-07-12 16:55:23 +02:00
Logger :: info ( 'Duplicates handled' , [ 'uid' => $uid , 'nurl' => $nurl ]);
return true ;
}
2018-01-09 15:40:45 +01:00
/**
2018-09-15 20:54:45 +02:00
* @ param integer $id contact id
* @ param string $network Optional network we are probing for
2019-03-07 01:13:39 +01:00
* @ param boolean $force Optional forcing of network probing ( otherwise we use the cached data )
2018-01-09 15:40:45 +01:00
* @ return boolean
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-01-09 15:40:45 +01:00
*/
2019-03-07 01:13:39 +01:00
public static function updateFromProbe ( $id , $network = '' , $force = false )
2018-01-09 15:40:45 +01:00
{
/*
2018-01-11 09:37:11 +01:00
Warning : Never ever fetch the public key via Probe :: uri and write it into the contacts .
2019-06-24 05:25:01 +02:00
This will reliably kill your communication with old Friendica contacts .
2018-01-11 09:37:11 +01:00
*/
2018-01-09 15:40:45 +01:00
2019-07-04 21:31:42 +02:00
// These fields aren't updated by this routine:
2019-07-12 16:55:23 +02:00
// 'xmpp', 'sensitive'
2019-07-04 21:31:42 +02:00
2020-06-01 23:52:31 +02:00
$fields = [ 'uid' , 'avatar' , 'name' , 'nick' , 'location' , 'keywords' , 'about' , 'subscribe' ,
2019-07-12 16:55:23 +02:00
'unsearchable' , 'url' , 'addr' , 'batch' , 'notify' , 'poll' , 'request' , 'confirm' , 'poco' ,
2020-05-22 06:19:32 +02:00
'network' , 'alias' , 'baseurl' , 'gsid' , 'forum' , 'prv' , 'contact-type' , 'pubkey' ];
2018-07-20 14:19:26 +02:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $id ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $contact )) {
2018-01-09 15:40:45 +01:00
return false ;
}
2019-03-07 01:13:39 +01:00
$uid = $contact [ 'uid' ];
unset ( $contact [ 'uid' ]);
2019-07-12 23:07:47 +02:00
$pubkey = $contact [ 'pubkey' ];
unset ( $contact [ 'pubkey' ]);
2019-03-07 01:13:39 +01:00
$contact [ 'photo' ] = $contact [ 'avatar' ];
unset ( $contact [ 'avatar' ]);
$ret = Probe :: uri ( $contact [ 'url' ], $network , $uid , ! $force );
2018-01-09 15:40:45 +01:00
2019-06-24 05:25:01 +02:00
$updated = DateTimeFormat :: utcNow ();
2019-04-09 13:28:45 +02:00
2019-07-13 09:25:01 +02:00
// We must not try to update relay contacts via probe. They are no real contacts.
// We check after the probing to be able to correct falsely detected contact types.
if (( $contact [ 'contact-type' ] == self :: TYPE_RELAY ) &&
( ! Strings :: compareLink ( $ret [ 'url' ], $contact [ 'url' ]) || in_array ( $ret [ 'network' ], [ Protocol :: FEED , Protocol :: PHANTOM ]))) {
self :: updateContact ( $id , $uid , $contact [ 'url' ], [ 'last-update' => $updated , 'success_update' => $updated ]);
Logger :: info ( 'Not updating relais' , [ 'id' => $id , 'url' => $contact [ 'url' ]]);
return true ;
}
2019-09-20 23:01:52 +02:00
// If Probe::uri fails the network code will be different ("feed" or "unkn")
if ( in_array ( $ret [ 'network' ], [ Protocol :: FEED , Protocol :: PHANTOM ]) && ( $ret [ 'network' ] != $contact [ 'network' ])) {
2019-06-24 05:25:01 +02:00
if ( $force && ( $uid == 0 )) {
self :: updateContact ( $id , $uid , $ret [ 'url' ], [ 'last-update' => $updated , 'failure_update' => $updated ]);
}
2018-01-09 15:40:45 +01:00
return false ;
}
2019-07-12 16:55:23 +02:00
if ( isset ( $ret [ 'hide' ]) && is_bool ( $ret [ 'hide' ])) {
$ret [ 'unsearchable' ] = $ret [ 'hide' ];
}
if ( isset ( $ret [ 'account-type' ]) && is_int ( $ret [ 'account-type' ])) {
2019-07-04 21:31:42 +02:00
$ret [ 'forum' ] = false ;
$ret [ 'prv' ] = false ;
$ret [ 'contact-type' ] = $ret [ 'account-type' ];
if ( $ret [ 'contact-type' ] == User :: ACCOUNT_TYPE_COMMUNITY ) {
$apcontact = APContact :: getByURL ( $ret [ 'url' ], false );
if ( isset ( $apcontact [ 'manually-approve' ])) {
$ret [ 'forum' ] = ( bool ) ! $apcontact [ 'manually-approve' ];
$ret [ 'prv' ] = ( bool ) ! $ret [ 'forum' ];
}
}
}
2019-07-12 23:07:47 +02:00
$new_pubkey = $ret [ 'pubkey' ];
2018-01-09 15:40:45 +01:00
$update = false ;
2019-07-12 16:55:23 +02:00
// make sure to not overwrite existing values with blank entries except some technical fields
$keep = [ 'batch' , 'notify' , 'poll' , 'request' , 'confirm' , 'poco' , 'baseurl' ];
2018-01-09 15:44:22 +01:00
foreach ( $ret as $key => $val ) {
2019-07-04 06:08:55 +02:00
if ( ! array_key_exists ( $key , $contact )) {
2019-03-07 01:13:39 +01:00
unset ( $ret [ $key ]);
2019-07-13 09:25:01 +02:00
} elseif (( $contact [ $key ] != '' ) && ( $val === '' ) && ! is_bool ( $ret [ $key ]) && ! in_array ( $key , $keep )) {
2018-01-11 09:26:30 +01:00
$ret [ $key ] = $contact [ $key ];
2019-03-07 01:13:39 +01:00
} elseif ( $ret [ $key ] != $contact [ $key ]) {
2018-01-09 15:40:45 +01:00
$update = true ;
2018-01-11 09:26:30 +01:00
}
2018-01-09 15:40:45 +01:00
}
2019-09-20 23:01:52 +02:00
if ( ! empty ( $ret [ 'photo' ]) && ( $ret [ 'network' ] != Protocol :: FEED )) {
2019-06-28 06:26:49 +02:00
self :: updateAvatar ( $ret [ 'photo' ], $uid , $id , $update || $force );
}
2019-06-23 11:27:40 +02:00
2018-01-09 15:40:45 +01:00
if ( ! $update ) {
2019-08-26 17:51:56 +02:00
if ( $force ) {
2019-06-24 05:25:01 +02:00
self :: updateContact ( $id , $uid , $ret [ 'url' ], [ 'last-update' => $updated , 'success_update' => $updated ]);
}
2020-01-16 07:43:21 +01:00
// Update the public contact
if ( $uid != 0 ) {
self :: updateFromProbeByURL ( $ret [ 'url' ]);
}
2018-01-09 15:40:45 +01:00
return true ;
}
2019-03-07 01:13:39 +01:00
$ret [ 'nurl' ] = Strings :: normaliseLink ( $ret [ 'url' ]);
2019-06-24 05:25:01 +02:00
$ret [ 'updated' ] = $updated ;
2019-07-12 23:07:47 +02:00
// Only fill the pubkey if it had been empty before. We have to prevent identity theft.
if ( empty ( $pubkey ) && ! empty ( $new_pubkey )) {
$ret [ 'pubkey' ] = $new_pubkey ;
}
if (( $ret [ 'addr' ] != $contact [ 'addr' ]) || ( ! empty ( $ret [ 'alias' ]) && ( $ret [ 'alias' ] != $contact [ 'alias' ]))) {
$ret [ 'uri-date' ] = DateTimeFormat :: utcNow ();
}
if (( $ret [ 'name' ] != $contact [ 'name' ]) || ( $ret [ 'nick' ] != $contact [ 'nick' ])) {
$ret [ 'name-date' ] = $updated ;
}
2019-06-24 05:25:01 +02:00
if ( $force && ( $uid == 0 )) {
$ret [ 'last-update' ] = $updated ;
$ret [ 'success_update' ] = $updated ;
}
2019-03-07 01:13:39 +01:00
unset ( $ret [ 'photo' ]);
2018-01-09 15:40:45 +01:00
2019-06-24 05:25:01 +02:00
self :: updateContact ( $id , $uid , $ret [ 'url' ], $ret );
2019-06-23 11:27:40 +02:00
2018-01-09 15:40:45 +01:00
return true ;
}
2019-07-04 21:31:42 +02:00
public static function updateFromProbeByURL ( $url , $force = false )
{
$id = self :: getIdForURL ( $url );
if ( empty ( $id )) {
2019-07-12 16:55:23 +02:00
return $id ;
2019-07-04 21:31:42 +02:00
}
self :: updateFromProbe ( $id , '' , $force );
2019-07-12 16:55:23 +02:00
return $id ;
2019-07-04 21:31:42 +02:00
}
2019-05-02 15:05:31 +02:00
/**
* Detects if a given contact array belongs to a legacy DFRN connection
*
* @ param array $contact
* @ return boolean
*/
2019-05-02 21:22:43 +02:00
public static function isLegacyDFRNContact ( $contact )
2019-05-02 15:05:31 +02:00
{
// Newer Friendica contacts are connected via AP, then these fields aren't set
return ! empty ( $contact [ 'dfrn-id' ]) || ! empty ( $contact [ 'issued-id' ]);
}
/**
* Detects the communication protocol for a given contact url .
* This is used to detect Friendica contacts that we can communicate via AP .
*
* @ param string $url contact url
* @ param string $network Network of that contact
* @ return string with protocol
*/
public static function getProtocol ( $url , $network )
{
if ( $network != Protocol :: DFRN ) {
return $network ;
}
$apcontact = APContact :: getByURL ( $url );
if ( ! empty ( $apcontact ) && ! empty ( $apcontact [ 'generator' ])) {
return Protocol :: ACTIVITYPUB ;
} else {
return $network ;
}
}
2018-01-09 15:40:45 +01:00
/**
* Takes a $uid and a url / handle and adds a new contact
2020-01-19 10:55:28 +01:00
*
2018-01-09 15:40:45 +01:00
* Currently if the contact is DFRN , interactive needs to be true , to redirect to the
* dfrn_request page .
*
2018-01-11 09:37:11 +01:00
* Otherwise this can be used to bulk add StatusNet contacts , Twitter contacts , etc .
2018-01-09 15:40:45 +01:00
*
* Returns an array
* $return [ 'success' ] boolean true if successful
* $return [ 'message' ] error text if success is false .
*
2020-01-19 07:05:23 +01:00
* Takes a $uid and a url / handle and adds a new contact
2020-06-10 16:36:42 +02:00
*
* @ param array $user The user the contact should be created for
* @ param string $url The profile URL of the contact
2018-01-09 15:40:45 +01:00
* @ param bool $interactive
* @ param string $network
2019-01-06 22:06:53 +01:00
* @ return array
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2020-06-10 16:36:42 +02:00
* @ throws HTTPException\NotFoundException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-01-09 15:40:45 +01:00
*/
2020-06-10 16:36:42 +02:00
public static function createFromProbe ( array $user , $url , $interactive = false , $network = '' )
2018-01-09 15:40:45 +01:00
{
2018-01-15 14:05:12 +01:00
$result = [ 'cid' => - 1 , 'success' => false , 'message' => '' ];
2018-01-09 15:40:45 +01:00
// remove ajax junk, e.g. Twitter
$url = str_replace ( '/#!/' , '/' , $url );
2018-01-27 17:13:41 +01:00
if ( ! Network :: isUrlAllowed ( $url )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] = DI :: l10n () -> t ( 'Disallowed profile URL.' );
2018-01-09 15:40:45 +01:00
return $result ;
}
2018-01-27 17:13:41 +01:00
if ( Network :: isUrlBlocked ( $url )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] = DI :: l10n () -> t ( 'Blocked domain' );
2018-01-09 15:40:45 +01:00
return $result ;
}
if ( ! $url ) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] = DI :: l10n () -> t ( 'Connect URL missing.' );
2018-01-09 15:40:45 +01:00
return $result ;
}
2018-01-15 14:05:12 +01:00
$arr = [ 'url' => $url , 'contact' => []];
2018-01-09 15:40:45 +01:00
2018-11-10 14:18:16 +01:00
Hook :: callAll ( 'follow' , $arr );
2018-01-09 15:40:45 +01:00
2018-01-30 19:51:09 +01:00
if ( empty ( $arr )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] = DI :: l10n () -> t ( 'The contact could not be added. Please check the relevant network credentials in your Settings -> Social Networks page.' );
2018-01-30 19:51:09 +01:00
return $result ;
}
2018-11-30 15:06:22 +01:00
if ( ! empty ( $arr [ 'contact' ][ 'name' ])) {
2018-01-09 15:40:45 +01:00
$ret = $arr [ 'contact' ];
} else {
2020-06-10 16:36:42 +02:00
$ret = Probe :: uri ( $url , $network , $user [ 'uid' ], false );
2018-01-09 15:40:45 +01:00
}
if (( $network != '' ) && ( $ret [ 'network' ] != $network )) {
2018-10-29 22:20:46 +01:00
Logger :: log ( 'Expected network ' . $network . ' does not match actual network ' . $ret [ 'network' ]);
2018-05-15 03:23:24 +02:00
return $result ;
2018-01-09 15:40:45 +01:00
}
2018-03-16 07:43:10 +01:00
// check if we already have a contact
// the poll url is more reliable than the profile url, as we may have
// indirect links or webfinger links
2020-06-10 16:36:42 +02:00
$condition = [ 'uid' => $user [ 'uid' ], 'poll' => [ $ret [ 'poll' ], Strings :: normaliseLink ( $ret [ 'poll' ])], 'network' => $ret [ 'network' ], 'pending' => false ];
2018-08-19 14:46:11 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'rel' ], $condition );
if ( ! DBA :: isResult ( $contact )) {
2020-06-25 02:57:47 +02:00
$condition = [ 'uid' => $user [ 'uid' ], 'nurl' => Strings :: normaliseLink ( $ret [ 'url' ]), 'network' => $ret [ 'network' ], 'pending' => false ];
2018-08-19 14:46:11 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'rel' ], $condition );
2018-03-16 07:43:10 +01:00
}
2020-06-25 02:57:47 +02:00
$protocol = self :: getProtocol ( $ret [ 'url' ], $ret [ 'network' ]);
2019-05-02 15:05:31 +02:00
if (( $protocol === Protocol :: DFRN ) && ! DBA :: isResult ( $contact )) {
2018-01-09 15:40:45 +01:00
if ( $interactive ) {
2019-12-16 00:39:54 +01:00
if ( strlen ( DI :: baseUrl () -> getUrlPath ())) {
2020-06-10 16:36:42 +02:00
$myaddr = bin2hex ( DI :: baseUrl () . '/profile/' . $user [ 'nickname' ]);
2018-01-09 15:40:45 +01:00
} else {
2020-06-10 16:36:42 +02:00
$myaddr = bin2hex ( $user [ 'nickname' ] . '@' . DI :: baseUrl () -> getHostname ());
2018-01-09 15:40:45 +01:00
}
2019-12-16 00:28:31 +01:00
DI :: baseUrl () -> redirect ( $ret [ 'request' ] . " &addr= $myaddr " );
2018-01-09 15:40:45 +01:00
// NOTREACHED
}
2020-01-19 21:21:13 +01:00
} elseif ( DI :: config () -> get ( 'system' , 'dfrn_only' ) && ( $ret [ 'network' ] != Protocol :: DFRN )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] = DI :: l10n () -> t ( 'This site is not configured to allow communications with other networks.' ) . EOL ;
$result [ 'message' ] .= DI :: l10n () -> t ( 'No compatible communication protocols or feeds were discovered.' ) . EOL ;
2018-01-09 15:40:45 +01:00
return $result ;
}
// This extra param just confuses things, remove it
2019-05-02 15:05:31 +02:00
if ( $protocol === Protocol :: DIASPORA ) {
2018-01-09 15:40:45 +01:00
$ret [ 'url' ] = str_replace ( '?absolute=true' , '' , $ret [ 'url' ]);
}
// do we have enough information?
2018-11-30 15:06:22 +01:00
if ( empty ( $ret [ 'name' ]) || empty ( $ret [ 'poll' ]) || ( empty ( $ret [ 'url' ]) && empty ( $ret [ 'addr' ]))) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'The profile address specified does not provide adequate information.' ) . EOL ;
2018-11-30 15:06:22 +01:00
if ( empty ( $ret [ 'poll' ])) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'No compatible communication protocols or feeds were discovered.' ) . EOL ;
2018-01-09 15:40:45 +01:00
}
2018-11-30 15:06:22 +01:00
if ( empty ( $ret [ 'name' ])) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'An author or name was not found.' ) . EOL ;
2018-01-09 15:40:45 +01:00
}
2018-11-30 15:06:22 +01:00
if ( empty ( $ret [ 'url' ])) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'No browser URL could be matched to this address.' ) . EOL ;
2018-01-09 15:40:45 +01:00
}
2020-06-25 02:57:47 +02:00
if ( strpos ( $ret [ 'url' ], '@' ) !== false ) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'Unable to match @-style Identity Address with a known protocol or email contact.' ) . EOL ;
$result [ 'message' ] .= DI :: l10n () -> t ( 'Use mailto: in front of address to force email check.' ) . EOL ;
2018-01-09 15:40:45 +01:00
}
return $result ;
}
2020-01-19 21:21:13 +01:00
if ( $protocol === Protocol :: OSTATUS && DI :: config () -> get ( 'system' , 'ostatus_disabled' )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'The profile address specified belongs to a network which has been disabled on this site.' ) . EOL ;
2018-01-09 15:40:45 +01:00
$ret [ 'notify' ] = '' ;
}
if ( ! $ret [ 'notify' ]) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'Limited profile. This person will be unable to receive direct/personal notifications from you.' ) . EOL ;
2018-01-09 15:40:45 +01:00
}
2019-05-02 15:05:31 +02:00
$writeable = ((( $protocol === Protocol :: OSTATUS ) && ( $ret [ 'notify' ])) ? 1 : 0 );
2018-01-09 15:40:45 +01:00
2019-05-02 15:05:31 +02:00
$subhub = (( $protocol === Protocol :: OSTATUS ) ? true : false );
2018-01-09 15:40:45 +01:00
2019-05-02 15:05:31 +02:00
$hidden = (( $protocol === Protocol :: MAIL ) ? 1 : 0 );
2018-01-09 15:40:45 +01:00
2019-11-02 17:24:54 +01:00
$pending = false ;
2019-11-02 17:20:17 +01:00
if ( $protocol == Protocol :: ACTIVITYPUB ) {
2020-06-25 02:57:47 +02:00
$apcontact = APContact :: getByURL ( $ret [ 'url' ], false );
2019-11-02 17:20:17 +01:00
if ( isset ( $apcontact [ 'manually-approve' ])) {
$pending = ( bool ) $apcontact [ 'manually-approve' ];
2019-12-09 22:19:20 +01:00
}
2019-11-02 17:20:17 +01:00
}
2019-01-09 14:23:11 +01:00
2019-05-02 15:05:31 +02:00
if ( in_array ( $protocol , [ Protocol :: MAIL , Protocol :: DIASPORA , Protocol :: ACTIVITYPUB ])) {
2018-01-09 15:40:45 +01:00
$writeable = 1 ;
}
2018-08-19 14:46:11 +02:00
if ( DBA :: isResult ( $contact )) {
2018-01-09 15:40:45 +01:00
// update contact
2018-08-19 14:46:11 +02:00
$new_relation = (( $contact [ 'rel' ] == self :: FOLLOWER ) ? self :: FRIEND : self :: SHARING );
2018-01-09 15:40:45 +01:00
2018-01-15 14:05:12 +01:00
$fields = [ 'rel' => $new_relation , 'subhub' => $subhub , 'readonly' => false ];
2018-08-19 14:46:11 +02:00
DBA :: update ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2018-01-09 15:40:45 +01:00
} else {
2019-05-02 15:05:31 +02:00
$new_relation = ( in_array ( $protocol , [ Protocol :: MAIL ]) ? self :: FRIEND : self :: SHARING );
2018-01-09 15:40:45 +01:00
// create contact record
2019-08-29 06:07:07 +02:00
self :: insert ([
2020-06-10 16:36:42 +02:00
'uid' => $user [ 'uid' ],
2018-01-27 03:38:34 +01:00
'created' => DateTimeFormat :: utcNow (),
2018-01-11 09:37:11 +01:00
'url' => $ret [ 'url' ],
2018-11-08 17:28:29 +01:00
'nurl' => Strings :: normaliseLink ( $ret [ 'url' ]),
2018-01-11 09:37:11 +01:00
'addr' => $ret [ 'addr' ],
'alias' => $ret [ 'alias' ],
'batch' => $ret [ 'batch' ],
'notify' => $ret [ 'notify' ],
'poll' => $ret [ 'poll' ],
'poco' => $ret [ 'poco' ],
'name' => $ret [ 'name' ],
'nick' => $ret [ 'nick' ],
'network' => $ret [ 'network' ],
2019-07-04 06:08:55 +02:00
'baseurl' => $ret [ 'baseurl' ],
2020-05-22 06:19:32 +02:00
'gsid' => $ret [ 'gsid' ] ? ? null ,
2019-05-02 15:05:31 +02:00
'protocol' => $protocol ,
2018-01-11 09:37:11 +01:00
'pubkey' => $ret [ 'pubkey' ],
'rel' => $new_relation ,
'priority' => $ret [ 'priority' ],
'writable' => $writeable ,
'hidden' => $hidden ,
'blocked' => 0 ,
'readonly' => 0 ,
2019-01-09 14:23:11 +01:00
'pending' => $pending ,
2018-01-11 09:37:11 +01:00
'subhub' => $subhub
]);
2018-01-09 15:40:45 +01:00
}
2020-06-10 16:36:42 +02:00
$contact = DBA :: selectFirst ( 'contact' , [], [ 'url' => $ret [ 'url' ], 'network' => $ret [ 'network' ], 'uid' => $user [ 'uid' ]]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $contact )) {
2020-01-18 20:52:34 +01:00
$result [ 'message' ] .= DI :: l10n () -> t ( 'Unable to retrieve contact information.' ) . EOL ;
2018-01-09 15:40:45 +01:00
return $result ;
}
2018-01-11 02:13:51 +01:00
$contact_id = $contact [ 'id' ];
2018-01-09 15:40:45 +01:00
$result [ 'cid' ] = $contact_id ;
2020-06-10 16:36:42 +02:00
Group :: addMember ( User :: getDefaultGroup ( $user [ 'uid' ], $contact [ " network " ]), $contact_id );
2018-01-09 15:40:45 +01:00
// Update the avatar
2020-06-10 16:36:42 +02:00
self :: updateAvatar ( $ret [ 'photo' ], $user [ 'uid' ], $contact_id );
2018-01-09 15:40:45 +01:00
// pull feed and consume it, which should subscribe to the hub.
Worker :: add ( PRIORITY_HIGH , " OnePoll " , $contact_id , " force " );
2020-06-10 16:36:42 +02:00
$owner = User :: getOwnerDataById ( $user [ 'uid' ]);
2018-01-09 15:40:45 +01:00
2018-08-19 14:46:11 +02:00
if ( DBA :: isResult ( $owner )) {
2019-05-02 15:05:31 +02:00
if ( in_array ( $protocol , [ Protocol :: OSTATUS , Protocol :: DFRN ])) {
2018-01-09 15:40:45 +01:00
// create a follow slap
2018-01-15 14:05:12 +01:00
$item = [];
2019-10-24 00:25:43 +02:00
$item [ 'verb' ] = Activity :: FOLLOW ;
2020-06-16 22:30:25 +02:00
$item [ 'gravity' ] = GRAVITY_ACTIVITY ;
2018-01-09 15:40:45 +01:00
$item [ 'follow' ] = $contact [ " url " ];
2018-08-01 07:29:58 +02:00
$item [ 'body' ] = '' ;
$item [ 'title' ] = '' ;
$item [ 'guid' ] = '' ;
2020-05-05 07:11:59 +02:00
$item [ 'uri-id' ] = 0 ;
2018-08-01 07:29:58 +02:00
$item [ 'attach' ] = '' ;
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
2018-08-19 14:46:11 +02:00
$slap = OStatus :: salmon ( $item , $owner );
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
2018-03-16 07:43:10 +01:00
if ( ! empty ( $contact [ 'notify' ])) {
2018-08-19 14:46:11 +02:00
Salmon :: slapper ( $owner , $contact [ 'notify' ], $slap );
2018-03-16 07:43:10 +01:00
}
2019-05-02 15:05:31 +02:00
} elseif ( $protocol == Protocol :: DIASPORA ) {
2020-06-10 16:36:42 +02:00
$ret = Diaspora :: sendShare ( $owner , $contact );
2018-10-29 22:20:46 +01:00
Logger :: log ( 'share returns: ' . $ret );
2019-05-02 15:05:31 +02:00
} elseif ( $protocol == Protocol :: ACTIVITYPUB ) {
2019-01-10 08:24:12 +01:00
$activity_id = ActivityPub\Transmitter :: activityIDFromContact ( $contact_id );
if ( empty ( $activity_id )) {
// This really should never happen
return false ;
}
2020-06-10 16:36:42 +02:00
$ret = ActivityPub\Transmitter :: sendActivity ( 'Follow' , $contact [ 'url' ], $user [ 'uid' ], $activity_id );
2018-10-29 22:20:46 +01:00
Logger :: log ( 'Follow returns: ' . $ret );
2018-01-09 15:40:45 +01:00
}
}
$result [ 'success' ] = true ;
return $result ;
}
2018-01-27 17:13:41 +01:00
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
/**
2020-01-19 07:05:23 +01:00
* Updated contact ' s SSL policy
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
*
2019-01-06 22:06:53 +01:00
* @ param array $contact Contact array
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
* @ param string $new_policy New policy , valid : self , full
*
* @ return array Contact array with updated values
2019-01-06 22:06:53 +01:00
* @ throws \Exception
Cleanups: isResult() more used, readability improved (#5608)
* [diaspora]: Maybe SimpleXMLElement is the right type-hint?
* Changes proposed + pre-renaming:
- pre-renamed $db -> $connection
- added TODOs for not allowing bad method invocations (there is a
BadMethodCallException in SPL)
* If no record is found, below $r[0] will fail with a E_NOTICE and the code
doesn't behave as expected.
* Ops, one more left ...
* Continued:
- added documentation for Contact::updateSslPolicy() method
- added type-hint for $contact of same method
- empty lines added + TODO where the bug origins that $item has no element 'body'
* Added empty lines for better readability
* Cleaned up:
- no more x() (deprecated) usage but empty() instead
- fixed mixing of space/tab indending
- merged else/if block goether in elseif() (lesser nested code blocks)
* Re-fixed DBM -> DBA switch
* Fixes/rewrites:
- use empty()/isset() instead of deprecated x()
- merged 2 nested if() blocks into one
- avoided nested if() block inside else block by rewriting it to elseif()
- $contact_id is an integer, let's test on > 0 here
- added a lot spaces and some empty lines for better readability
* Rewrite:
- moved all CONTACT_* constants from boot.php to Contact class
* CR request:
- renamed Contact::CONTACT_IS_* -> Contact::* ;-)
* Rewrites:
- moved PAGE_* to Friendica\Model\Profile class
- fixed mixure with "Contact::* rewrite"
* Ops, one still there (return is no function)
* Rewrite to Proxy class:
- introduced new Friendica\Network\Proxy class for in exchange of proxy_*()
functions
- moved also all PROXY_* constants there as Proxy::*
- removed now no longer needed mod/proxy.php loading as composer's auto-load
will do this for us
- renamed those proxy_*() functions to better names:
+ proxy_init() -> Proxy::init() (public)
+ proxy_url() -> Proxy::proxifyUrl() (public)
+ proxy_parse_html() -> Proxy::proxifyHtml() (public)
+ proxy_is_local_image() -> Proxy::isLocalImage() (private)
+ proxy_parse_query() -> Proxy::parseQuery() (private)
+ proxy_img_cb() -> Proxy::replaceUrl() (private)
* CR request:
- moved all PAGE_* constants to Friendica\Model\Contact class
- fixed all references of both classes
* Ops, need to set $a here ...
* CR request:
- moved Proxy class to Friendica\Module
- extended BaseModule
* Ops, no need for own instance of $a when self::getApp() is around.
* Proxy-rewrite:
- proxy_url() and proxy_parse_html() are both non-module functions (now
methods)
- so they must be splitted into a seperate class
- also the SIZE_* and DEFAULT_TIME constants are both not relevant to module
* No instances from utility classes
* Fixed error:
- proxify*() is now located in `Friendica\Util\ProxyUtils`
* Moved back to original place, ops? How did they move here? Well, it was not
intended by me.
* Removed duplicate (left-over from split) constants and static array. Thank to
MrPetovan finding it.
* Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils.
* Rewrite:
- stopped using deprecated NETWORK_* constants, now Protocol::* should be used
- still left them intact for slow/lazy developers ...
* Ops, was added accidentally ...
* Ops, why these wrong moves?
* Ops, one to much (thanks to MrPetovan)
* Ops, wrong moving ...
* moved back to original place ...
* spaces added
* empty lines add for better readability.
* convertered spaces -> tab for code indenting.
* CR request: Add space between if and brace.
* CR requests fixed + move reverted
- ops, src/Module/*.php has been moved to src/Network/ accidentally
- reverted some parts in src/Database/DBA.php as pointed out by Annando
- removed internal TODO items
- added some spaces for better readability
2018-08-24 07:05:49 +02:00
*/
public static function updateSslPolicy ( array $contact , $new_policy )
2018-01-27 17:13:41 +01:00
{
$ssl_changed = false ;
2019-04-08 21:12:10 +02:00
if (( intval ( $new_policy ) == BaseURL :: SSL_POLICY_SELFSIGN || $new_policy === 'self' ) && strstr ( $contact [ 'url' ], 'https:' )) {
2018-01-27 17:13:41 +01:00
$ssl_changed = true ;
$contact [ 'url' ] = str_replace ( 'https:' , 'http:' , $contact [ 'url' ]);
$contact [ 'request' ] = str_replace ( 'https:' , 'http:' , $contact [ 'request' ]);
$contact [ 'notify' ] = str_replace ( 'https:' , 'http:' , $contact [ 'notify' ]);
$contact [ 'poll' ] = str_replace ( 'https:' , 'http:' , $contact [ 'poll' ]);
$contact [ 'confirm' ] = str_replace ( 'https:' , 'http:' , $contact [ 'confirm' ]);
$contact [ 'poco' ] = str_replace ( 'https:' , 'http:' , $contact [ 'poco' ]);
}
2019-04-08 21:12:10 +02:00
if (( intval ( $new_policy ) == BaseURL :: SSL_POLICY_FULL || $new_policy === 'full' ) && strstr ( $contact [ 'url' ], 'http:' )) {
2018-01-27 17:13:41 +01:00
$ssl_changed = true ;
$contact [ 'url' ] = str_replace ( 'http:' , 'https:' , $contact [ 'url' ]);
$contact [ 'request' ] = str_replace ( 'http:' , 'https:' , $contact [ 'request' ]);
$contact [ 'notify' ] = str_replace ( 'http:' , 'https:' , $contact [ 'notify' ]);
$contact [ 'poll' ] = str_replace ( 'http:' , 'https:' , $contact [ 'poll' ]);
$contact [ 'confirm' ] = str_replace ( 'http:' , 'https:' , $contact [ 'confirm' ]);
$contact [ 'poco' ] = str_replace ( 'http:' , 'https:' , $contact [ 'poco' ]);
}
if ( $ssl_changed ) {
$fields = [ 'url' => $contact [ 'url' ], 'request' => $contact [ 'request' ],
'notify' => $contact [ 'notify' ], 'poll' => $contact [ 'poll' ],
'confirm' => $contact [ 'confirm' ], 'poco' => $contact [ 'poco' ]];
2018-07-20 14:19:26 +02:00
DBA :: update ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2018-01-27 17:13:41 +01:00
}
return $contact ;
}
2018-01-28 12:18:08 +01:00
2019-05-20 00:43:19 +02:00
/**
* @ param array $importer Owner ( local user ) data
* @ param array $contact Existing owner - specific contact data we want to expand the relationship with . Optional .
* @ param array $datarray An item - like array with at least the 'author-id' and 'author-url' keys for the contact . Mandatory .
* @ param bool $sharing True : Contact is now sharing with Owner ; False : Contact is now following Owner ( default )
* @ param string $note Introduction additional message
2019-05-20 00:46:29 +02:00
* @ return bool | null True : follow request is accepted ; False : relationship is rejected ; Null : relationship is pending
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-05-20 00:43:19 +02:00
* @ throws \ImagickException
*/
public static function addRelationship ( array $importer , array $contact , array $datarray , $sharing = false , $note = '' )
{
2018-08-02 07:21:01 +02:00
// Should always be set
if ( empty ( $datarray [ 'author-id' ])) {
2019-05-20 00:46:29 +02:00
return false ;
2018-01-28 12:18:08 +01:00
}
2019-06-19 19:05:29 +02:00
$fields = [ 'url' , 'name' , 'nick' , 'avatar' , 'photo' , 'network' , 'blocked' ];
2018-08-02 07:21:01 +02:00
$pub_contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $datarray [ 'author-id' ]]);
if ( ! DBA :: isResult ( $pub_contact )) {
// Should never happen
2019-05-20 00:46:29 +02:00
return false ;
2018-08-02 07:21:01 +02:00
}
2019-05-20 22:34:17 +02:00
// Contact is blocked at node-level
if ( self :: isBlocked ( $datarray [ 'author-id' ])) {
2019-05-20 00:46:58 +02:00
return false ;
}
2019-10-16 14:35:14 +02:00
$url = ( $datarray [ 'author-link' ] ? ? '' ) ? : $pub_contact [ 'url' ];
2018-08-02 07:21:01 +02:00
$name = $pub_contact [ 'name' ];
2019-10-16 14:35:14 +02:00
$photo = ( $pub_contact [ 'avatar' ] ? ? '' ) ? : $pub_contact [ " photo " ];
2018-08-02 07:21:01 +02:00
$nick = $pub_contact [ 'nick' ];
$network = $pub_contact [ 'network' ];
2019-08-26 17:51:56 +02:00
// Ensure that we don't create a new contact when there already is one
$cid = self :: getIdForURL ( $url , $importer [ 'uid' ]);
if ( ! empty ( $cid )) {
$contact = DBA :: selectFirst ( 'contact' , [], [ 'id' => $cid ]);
}
2019-05-20 00:43:19 +02:00
if ( ! empty ( $contact )) {
2019-08-26 17:51:56 +02:00
if ( ! empty ( $contact [ 'pending' ])) {
Logger :: info ( 'Pending contact request already exists.' , [ 'url' => $url , 'uid' => $importer [ 'uid' ]]);
return null ;
}
2019-06-19 19:05:29 +02:00
// Contact is blocked at user-level
if ( ! empty ( $contact [ 'id' ]) && ! empty ( $importer [ 'id' ]) &&
self :: isBlockedByUser ( $contact [ 'id' ], $importer [ 'id' ])) {
return false ;
}
2019-05-20 22:34:17 +02:00
2019-05-03 07:54:40 +02:00
// Make sure that the existing contact isn't archived
self :: unmarkForArchival ( $contact );
2018-07-25 04:53:46 +02:00
if (( $contact [ 'rel' ] == self :: SHARING )
|| ( $sharing && $contact [ 'rel' ] == self :: FOLLOWER )) {
2019-05-03 07:54:40 +02:00
DBA :: update ( 'contact' , [ 'rel' => self :: FRIEND , 'writable' => true , 'pending' => false ],
2018-01-28 12:18:08 +01:00
[ 'id' => $contact [ 'id' ], 'uid' => $importer [ 'uid' ]]);
}
2018-09-15 20:54:45 +02:00
2019-09-21 14:39:07 +02:00
// Ensure to always have the correct network type, independent from the connection request method
self :: updateFromProbe ( $contact [ 'id' ], '' , true );
2019-05-20 00:46:29 +02:00
return true ;
2018-01-28 12:18:08 +01:00
} else {
2019-05-20 00:43:19 +02:00
// send email notification to owner?
2018-11-08 17:28:29 +01:00
if ( DBA :: exists ( 'contact' , [ 'nurl' => Strings :: normaliseLink ( $url ), 'uid' => $importer [ 'uid' ], 'pending' => true ])) {
2018-10-29 22:20:46 +01:00
Logger :: log ( 'ignoring duplicated connection request from pending contact ' . $url );
2019-05-20 00:46:29 +02:00
return null ;
2018-04-18 07:02:59 +02:00
}
2019-05-20 00:46:29 +02:00
2018-01-28 12:18:08 +01:00
// create contact record
2019-05-20 00:43:19 +02:00
DBA :: insert ( 'contact' , [
'uid' => $importer [ 'uid' ],
'created' => DateTimeFormat :: utcNow (),
'url' => $url ,
'nurl' => Strings :: normaliseLink ( $url ),
'name' => $name ,
'nick' => $nick ,
'photo' => $photo ,
'network' => $network ,
'rel' => self :: FOLLOWER ,
'blocked' => 0 ,
'readonly' => 0 ,
'pending' => 1 ,
'writable' => 1 ,
]);
2018-01-28 12:18:08 +01:00
2019-09-21 14:39:07 +02:00
$contact_id = DBA :: lastInsertId ();
// Ensure to always have the correct network type, independent from the connection request method
self :: updateFromProbe ( $contact_id , '' , true );
Contact :: updateAvatar ( $photo , $importer [ " uid " ], $contact_id , true );
2018-07-10 14:27:56 +02:00
2019-09-21 14:39:07 +02:00
$contact_record = DBA :: selectFirst ( 'contact' , [ 'id' , 'network' , 'name' , 'url' , 'photo' ], [ 'id' => $contact_id ]);
2018-01-28 12:18:08 +01:00
/// @TODO Encapsulate this into a function/method
2018-02-14 06:05:00 +01:00
$fields = [ 'uid' , 'username' , 'email' , 'page-flags' , 'notify-flags' , 'language' ];
2018-07-20 14:19:26 +02:00
$user = DBA :: selectFirst ( 'user' , $fields , [ 'uid' => $importer [ 'uid' ]]);
2019-01-06 18:37:48 +01:00
if ( DBA :: isResult ( $user ) && ! in_array ( $user [ 'page-flags' ], [ User :: PAGE_FLAGS_SOAPBOX , User :: PAGE_FLAGS_FREELOVE , User :: PAGE_FLAGS_COMMUNITY ])) {
2018-01-28 12:18:08 +01:00
// create notification
2018-11-08 14:45:46 +01:00
$hash = Strings :: getRandomHex ();
2018-01-28 12:18:08 +01:00
if ( is_array ( $contact_record )) {
2018-07-20 14:19:26 +02:00
DBA :: insert ( 'intro' , [ 'uid' => $importer [ 'uid' ], 'contact-id' => $contact_record [ 'id' ],
2019-05-05 11:17:45 +02:00
'blocked' => false , 'knowyou' => false , 'note' => $note ,
2018-02-04 02:44:19 +01:00
'hash' => $hash , 'datetime' => DateTimeFormat :: utcNow ()]);
2018-01-28 12:18:08 +01:00
}
Group :: addMember ( User :: getDefaultGroup ( $importer [ 'uid' ], $contact_record [ " network " ]), $contact_record [ 'id' ]);
2020-02-04 22:03:45 +01:00
if (( $user [ 'notify-flags' ] & Type :: INTRO ) &&
2019-01-06 18:37:48 +01:00
in_array ( $user [ 'page-flags' ], [ User :: PAGE_FLAGS_NORMAL ])) {
2018-01-28 12:18:08 +01:00
notification ([
2020-02-04 22:03:45 +01:00
'type' => Type :: INTRO ,
2018-02-14 06:05:00 +01:00
'notify_flags' => $user [ 'notify-flags' ],
'language' => $user [ 'language' ],
'to_name' => $user [ 'username' ],
'to_email' => $user [ 'email' ],
'uid' => $user [ 'uid' ],
2020-01-29 22:48:16 +01:00
'link' => DI :: baseUrl () . '/notifications/intros' ,
2020-01-18 20:52:34 +01:00
'source_name' => (( strlen ( stripslashes ( $contact_record [ 'name' ]))) ? stripslashes ( $contact_record [ 'name' ]) : DI :: l10n () -> t ( '[Name Withheld]' )),
2018-01-28 12:18:08 +01:00
'source_link' => $contact_record [ 'url' ],
'source_photo' => $contact_record [ 'photo' ],
2019-10-24 00:25:43 +02:00
'verb' => ( $sharing ? Activity :: FRIEND : Activity :: FOLLOW ),
2018-01-28 12:18:08 +01:00
'otype' => 'intro'
]);
}
2019-01-06 18:37:48 +01:00
} elseif ( DBA :: isResult ( $user ) && in_array ( $user [ 'page-flags' ], [ User :: PAGE_FLAGS_SOAPBOX , User :: PAGE_FLAGS_FREELOVE , User :: PAGE_FLAGS_COMMUNITY ])) {
2020-03-08 00:19:19 +01:00
if (( $user [ 'page-flags' ] == User :: PAGE_FLAGS_FREELOVE ) && ( $network != Protocol :: DIASPORA )) {
2020-06-10 16:36:42 +02:00
self :: createFromProbe ( $importer , $url , false , $network );
2020-03-08 00:19:19 +01:00
}
2018-08-19 14:46:11 +02:00
$condition = [ 'uid' => $importer [ 'uid' ], 'url' => $url , 'pending' => true ];
2020-03-08 00:19:19 +01:00
$fields = [ 'pending' => false ];
if ( $user [ 'page-flags' ] == User :: PAGE_FLAGS_FREELOVE ) {
$fields [ 'rel' ] = Contact :: FRIEND ;
}
DBA :: update ( 'contact' , $fields , $condition );
2018-10-05 08:35:50 +02:00
2019-05-20 00:46:29 +02:00
return true ;
2018-01-28 12:18:08 +01:00
}
}
2019-05-20 00:46:29 +02:00
return null ;
2018-01-28 12:18:08 +01:00
}
2018-07-25 04:53:46 +02:00
public static function removeFollower ( $importer , $contact , array $datarray = [], $item = " " )
{
if (( $contact [ 'rel' ] == self :: FRIEND ) || ( $contact [ 'rel' ] == self :: SHARING )) {
DBA :: update ( 'contact' , [ 'rel' => self :: SHARING ], [ 'id' => $contact [ 'id' ]]);
2018-01-28 12:18:08 +01:00
} else {
Contact :: remove ( $contact [ 'id' ]);
}
}
2018-07-25 04:53:46 +02:00
public static function removeSharer ( $importer , $contact , array $datarray = [], $item = " " )
{
if (( $contact [ 'rel' ] == self :: FRIEND ) || ( $contact [ 'rel' ] == self :: FOLLOWER )) {
DBA :: update ( 'contact' , [ 'rel' => self :: FOLLOWER ], [ 'id' => $contact [ 'id' ]]);
2018-01-28 12:18:08 +01:00
} else {
Contact :: remove ( $contact [ 'id' ]);
}
}
2018-01-25 02:47:33 +01:00
/**
2020-01-19 07:05:23 +01:00
* Create a birthday event .
2018-01-25 02:47:33 +01:00
*
* Update the year and the birthday .
*/
public static function updateBirthdays ()
{
2018-11-22 06:15:44 +01:00
$condition = [
' `bd` != " "
AND `bd` > " 0001-01-01 "
AND SUBSTRING ( `bd` , 1 , 4 ) != `bdyear`
AND ( `contact` . `rel` = ? OR `contact` . `rel` = ? )
AND NOT `contact` . `pending`
AND NOT `contact` . `hidden`
AND NOT `contact` . `blocked`
AND NOT `contact` . `archive`
AND NOT `contact` . `deleted` ' ,
Contact :: SHARING ,
Contact :: FRIEND
];
$contacts = DBA :: select ( 'contact' , [ 'id' , 'uid' , 'name' , 'url' , 'bd' ], $condition );
while ( $contact = DBA :: fetch ( $contacts )) {
Logger :: log ( 'update_contact_birthday: ' . $contact [ 'bd' ]);
$nextbd = DateTimeFormat :: utcNow ( 'Y' ) . substr ( $contact [ 'bd' ], 4 );
if ( Event :: createBirthday ( $contact , $nextbd )) {
2018-01-25 02:47:33 +01:00
// update bdyear
2018-11-22 06:15:44 +01:00
DBA :: update (
'contact' ,
[ 'bdyear' => substr ( $nextbd , 0 , 4 ), 'bd' => $nextbd ],
[ 'id' => $contact [ 'id' ]]
2018-01-25 02:47:33 +01:00
);
}
}
2020-04-28 09:10:18 +02:00
DBA :: close ( $contacts );
2018-01-25 02:47:33 +01:00
}
2018-02-26 01:45:04 +01:00
/**
* Remove the unavailable contact ids from the provided list
*
* @ param array $contact_ids Contact id list
2020-01-05 23:07:33 +01:00
* @ return array
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-02-26 01:45:04 +01:00
*/
2020-01-05 23:07:33 +01:00
public static function pruneUnavailable ( array $contact_ids )
2018-02-26 01:45:04 +01:00
{
if ( empty ( $contact_ids )) {
2020-01-05 23:07:33 +01:00
return [];
2018-02-26 01:45:04 +01:00
}
2020-01-05 23:07:33 +01:00
$contacts = Contact :: selectToArray ([ 'id' ], [
'id' => $contact_ids ,
'blocked' => false ,
'pending' => false ,
'archive' => false ,
]);
2018-03-03 13:41:49 +01:00
2020-01-05 23:07:33 +01:00
return array_column ( $contacts , 'id' );
2018-02-26 01:45:04 +01:00
}
2018-06-01 08:46:34 +02:00
/**
2020-01-19 07:05:23 +01:00
* Returns a magic link to authenticate remote visitors
2018-06-01 08:46:34 +02:00
*
2019-01-06 22:06:53 +01:00
* @ todo check if the return is either a fully qualified URL or a relative path to Friendica basedir
2018-10-20 01:00:01 +02:00
*
2018-06-01 08:46:34 +02:00
* @ param string $contact_url The address of the target contact profile
2019-01-06 22:06:53 +01:00
* @ param string $url An url that we will be redirected to after the authentication
2018-06-01 08:46:34 +02:00
*
* @ return string with " redir " link
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-06-01 08:46:34 +02:00
*/
public static function magicLink ( $contact_url , $url = '' )
{
2019-09-28 20:09:11 +02:00
if ( ! Session :: isAuthenticated ()) {
2018-12-08 21:28:01 +01:00
return $url ? : $contact_url ; // Equivalent to: ($url != '') ? $url : $contact_url;
}
2019-04-12 14:50:44 +02:00
$data = self :: getProbeDataFromDatabase ( $contact_url );
if ( empty ( $data )) {
2018-06-02 15:00:47 +02:00
return $url ? : $contact_url ; // Equivalent to: ($url != '') ? $url : $contact_url;
2018-06-01 08:46:34 +02:00
}
2019-06-11 01:10:39 +02:00
// Prevents endless loop in case only a non-public contact exists for the contact URL
unset ( $data [ 'uid' ]);
2019-09-06 14:33:19 +02:00
return self :: magicLinkByContact ( $data , $url ? : $contact_url );
2018-06-01 08:46:34 +02:00
}
/**
2020-01-19 07:05:23 +01:00
* Returns a magic link to authenticate remote visitors
2018-06-01 08:46:34 +02:00
*
* @ param integer $cid The contact id of the target contact profile
2019-01-06 22:06:53 +01:00
* @ param string $url An url that we will be redirected to after the authentication
2018-06-01 08:46:34 +02:00
*
* @ return string with " redir " link
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-06-01 08:46:34 +02:00
*/
public static function magicLinkbyId ( $cid , $url = '' )
{
2018-07-20 14:19:26 +02:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'network' , 'url' , 'uid' ], [ 'id' => $cid ]);
2018-06-01 08:46:34 +02:00
2019-02-22 03:29:22 +01:00
return self :: magicLinkByContact ( $contact , $url );
2019-01-02 17:47:53 +01:00
}
2018-07-02 07:41:55 +02:00
/**
2020-01-19 07:05:23 +01:00
* Returns a magic link to authenticate remote visitors
2018-07-02 07:41:55 +02:00
*
2019-01-06 22:06:53 +01:00
* @ param array $contact The contact array with " uid " , " network " and " url "
* @ param string $url An url that we will be redirected to after the authentication
2018-07-02 07:41:55 +02:00
*
* @ return string with " redir " link
2020-03-09 16:11:06 +01:00
* @ throws HTTPException\InternalServerErrorException
2019-01-06 22:06:53 +01:00
* @ throws \ImagickException
2018-07-02 07:41:55 +02:00
*/
2019-02-22 03:29:22 +01:00
public static function magicLinkByContact ( $contact , $url = '' )
2018-07-02 07:41:55 +02:00
{
2019-09-10 06:04:07 +02:00
$destination = $url ? : $contact [ 'url' ]; // Equivalent to ($url != '') ? $url : $contact['url'];
2019-09-28 20:09:11 +02:00
if ( ! Session :: isAuthenticated () || ( $contact [ 'network' ] != Protocol :: DFRN )) {
2019-09-10 06:04:07 +02:00
return $destination ;
2018-06-01 08:46:34 +02:00
}
2018-06-02 10:42:46 +02:00
// Only redirections to the same host do make sense
if (( $url != '' ) && ( parse_url ( $url , PHP_URL_HOST ) != parse_url ( $contact [ 'url' ], PHP_URL_HOST ))) {
return $url ;
}
2019-05-10 09:38:10 +02:00
if ( ! empty ( $contact [ 'uid' ])) {
2018-06-02 10:05:06 +02:00
return self :: magicLink ( $contact [ 'url' ], $url );
}
2019-05-10 09:38:10 +02:00
if ( empty ( $contact [ 'id' ])) {
2019-09-10 06:04:07 +02:00
return $destination ;
2019-05-10 09:38:10 +02:00
}
2018-07-02 07:41:55 +02:00
$redirect = 'redir/' . $contact [ 'id' ];
2018-06-01 08:46:34 +02:00
2019-09-24 23:44:37 +02:00
if (( $url != '' ) && ! Strings :: compareLink ( $contact [ 'url' ], $url )) {
2018-06-01 08:46:34 +02:00
$redirect .= '?url=' . $url ;
}
return $redirect ;
2018-07-28 01:25:57 +02:00
}
2019-02-23 21:26:06 +01:00
2019-03-14 19:44:41 +01:00
/**
* Remove a contact from all groups
*
* @ param integer $contact_id
*
* @ return boolean Success
*/
2019-02-23 21:26:06 +01:00
public static function removeFromGroups ( $contact_id )
{
return DBA :: delete ( 'group_member' , [ 'contact-id' => $contact_id ]);
}
2019-03-14 19:44:41 +01:00
/**
* Is the contact a forum ?
*
* @ param integer $contactid ID of the contact
*
* @ return boolean " true " if it is a forum
*/
public static function isForum ( $contactid )
{
$fields = [ 'forum' , 'prv' ];
$condition = [ 'id' => $contactid ];
$contact = DBA :: selectFirst ( 'contact' , $fields , $condition );
if ( ! DBA :: isResult ( $contact )) {
return false ;
}
// Is it a forum?
return ( $contact [ 'forum' ] || $contact [ 'prv' ]);
}
2019-06-11 03:29:11 +02:00
/**
* Can the remote contact receive private messages ?
*
* @ param array $contact
* @ return bool
*/
public static function canReceivePrivateMessages ( array $contact )
{
$protocol = $contact [ 'network' ] ? ? $contact [ 'protocol' ] ? ? Protocol :: PHANTOM ;
$self = $contact [ 'self' ] ? ? false ;
return in_array ( $protocol , [ Protocol :: DFRN , Protocol :: DIASPORA , Protocol :: ACTIVITYPUB ]) && ! $self ;
}
2017-11-19 22:55:28 +01:00
}