2017-11-19 16:55:28 -05:00
< ? php
/**
2017-12-07 09:04:24 -05:00
* @ file src / Model / Contact . php
2017-11-19 16:55:28 -05:00
*/
2017-12-07 09:04:24 -05:00
namespace Friendica\Model ;
2017-11-19 16:55:28 -05:00
use Friendica\BaseObject ;
2018-01-17 13:42:40 -05:00
use Friendica\Core\Addon ;
2018-01-09 10:02:53 -05:00
use Friendica\Core\Config ;
2018-01-22 09:54:13 -05:00
use Friendica\Core\L10n ;
2017-11-19 16:55:28 -05:00
use Friendica\Core\PConfig ;
use Friendica\Core\System ;
use Friendica\Core\Worker ;
2018-07-20 08:19:26 -04:00
use Friendica\Database\DBA ;
2017-11-19 16:55:28 -05:00
use Friendica\Database\DBM ;
2018-01-24 20:47:33 -05:00
use Friendica\Network\Probe ;
2018-07-19 22:15:21 -04:00
use Friendica\Object\Image ;
2018-01-24 20:47:33 -05:00
use Friendica\Protocol\Diaspora ;
2017-11-19 16:55:28 -05:00
use Friendica\Protocol\OStatus ;
2018-01-09 09:44:22 -05:00
use Friendica\Protocol\PortableContact ;
2017-12-02 09:32:45 -05:00
use Friendica\Protocol\Salmon ;
2018-01-26 21:38:34 -05:00
use Friendica\Util\DateTimeFormat ;
2018-01-26 23:51:41 -05:00
use Friendica\Util\Network ;
2017-11-19 16:55:28 -05:00
require_once 'boot.php' ;
2017-12-17 15:24:57 -05:00
require_once 'include/dba.php' ;
2017-11-19 16:55:28 -05:00
require_once 'include/text.php' ;
2017-11-22 09:02:55 -05:00
/**
* @ brief functions for interacting with a contact
*/
2017-11-19 16:55:28 -05:00
class Contact extends BaseObject
{
2017-12-09 13:31:00 -05:00
/**
* @ brief Returns a list of contacts belonging in a group
*
* @ param int $gid
* @ return array
*/
public static function getByGroupId ( $gid )
{
$return = [];
if ( intval ( $gid )) {
2018-07-20 08:19:26 -04:00
$stmt = DBA :: p ( ' SELECT `group_member` . `contact-id` , `contact` .*
2017-12-09 13:31:00 -05:00
FROM `contact`
INNER JOIN `group_member`
ON `contact` . `id` = `group_member` . `contact-id`
WHERE `gid` = ?
2017-12-14 22:47:58 -05:00
AND `contact` . `uid` = ?
2017-12-09 13:31:00 -05:00
AND NOT `contact` . `self`
AND NOT `contact` . `blocked`
AND NOT `contact` . `pending`
ORDER BY `contact` . `name` ASC ' ,
$gid ,
local_user ()
);
if ( DBM :: is_result ( $stmt )) {
2018-07-20 22:03:40 -04:00
$return = DBA :: toArray ( $stmt );
2017-12-09 13:31:00 -05:00
}
}
return $return ;
}
/**
* @ brief Returns the count of OStatus contacts in a group
*
* @ param int $gid
* @ return int
*/
public static function getOStatusCountByGroupId ( $gid )
{
$return = 0 ;
if ( intval ( $gid )) {
2018-07-20 22:01:53 -04:00
$contacts = DBA :: fetchFirst ( ' SELECT COUNT ( * ) AS `count`
2017-12-09 13:31:00 -05:00
FROM `contact`
INNER JOIN `group_member`
ON `contact` . `id` = `group_member` . `contact-id`
WHERE `gid` = ?
2017-12-14 22:47:58 -05:00
AND `contact` . `uid` = ?
2017-12-09 13:31:00 -05:00
AND `contact` . `network` = ?
AND `contact` . `notify` != " " ' ,
$gid ,
local_user (),
NETWORK_OSTATUS
);
$return = $contacts [ 'count' ];
}
return $return ;
}
2017-12-03 22:27:49 -05:00
/**
* Creates the self - contact for the provided user id
*
* @ param int $uid
* @ return bool Operation success
*/
public static function createSelfFromUserId ( $uid )
{
// Only create the entry if it doesn't exist yet
2018-07-20 08:19:26 -04:00
if ( DBA :: exists ( 'contact' , [ 'uid' => $uid , 'self' => true ])) {
2017-12-03 22:27:49 -05:00
return true ;
}
2018-07-20 08:19:26 -04:00
$user = DBA :: selectFirst ( 'user' , [ 'uid' , 'username' , 'nickname' ], [ 'uid' => $uid ]);
2017-12-03 22:27:49 -05:00
if ( ! DBM :: is_result ( $user )) {
return false ;
}
2018-07-20 08:19:26 -04:00
$return = DBA :: insert ( 'contact' , [
2017-12-03 22:27:49 -05:00
'uid' => $user [ 'uid' ],
2018-01-26 21:38:34 -05:00
'created' => DateTimeFormat :: utcNow (),
2017-12-03 22:27:49 -05:00
'self' => 1 ,
'name' => $user [ 'username' ],
'nick' => $user [ 'nickname' ],
'photo' => System :: baseUrl () . '/photo/profile/' . $user [ 'uid' ] . '.jpg' ,
'thumb' => System :: baseUrl () . '/photo/avatar/' . $user [ 'uid' ] . '.jpg' ,
'micro' => System :: baseUrl () . '/photo/micro/' . $user [ 'uid' ] . '.jpg' ,
'blocked' => 0 ,
'pending' => 0 ,
'url' => System :: baseUrl () . '/profile/' . $user [ 'nickname' ],
'nurl' => normalise_link ( System :: baseUrl () . '/profile/' . $user [ 'nickname' ]),
'addr' => $user [ 'nickname' ] . '@' . substr ( System :: baseUrl (), strpos ( System :: baseUrl (), '://' ) + 3 ),
'request' => System :: baseUrl () . '/dfrn_request/' . $user [ 'nickname' ],
'notify' => System :: baseUrl () . '/dfrn_notify/' . $user [ 'nickname' ],
'poll' => System :: baseUrl () . '/dfrn_poll/' . $user [ 'nickname' ],
'confirm' => System :: baseUrl () . '/dfrn_confirm/' . $user [ 'nickname' ],
'poco' => System :: baseUrl () . '/poco/' . $user [ 'nickname' ],
2018-01-26 21:38:34 -05:00
'name-date' => DateTimeFormat :: utcNow (),
'uri-date' => DateTimeFormat :: utcNow (),
'avatar-date' => DateTimeFormat :: utcNow (),
2017-12-03 22:27:49 -05:00
'closeness' => 0
]);
return $return ;
}
2018-03-24 02:15:18 -04:00
/**
* Updates the self - contact for the provided user id
*
* @ param int $uid
* @ param boolean $update_avatar Force the avatar update
*/
public static function updateSelfFromUserID ( $uid , $update_avatar = false )
{
$fields = [ 'id' , 'name' , 'nick' , 'location' , 'about' , 'keywords' , 'gender' , 'avatar' ,
2018-03-25 04:20:13 -04:00
'xmpp' , 'contact-type' , 'forum' , 'prv' , 'avatar-date' , 'nurl' ];
2018-07-20 08:19:26 -04:00
$self = DBA :: selectFirst ( 'contact' , $fields , [ 'uid' => $uid , 'self' => true ]);
2018-03-24 02:15:18 -04:00
if ( ! DBM :: is_result ( $self )) {
return ;
}
$fields = [ 'nickname' , 'page-flags' , 'account-type' ];
2018-07-20 08:19:26 -04:00
$user = DBA :: selectFirst ( 'user' , $fields , [ 'uid' => $uid ]);
2018-03-24 02:15:18 -04:00
if ( ! DBM :: is_result ( $user )) {
return ;
}
$fields = [ 'name' , 'photo' , 'thumb' , 'about' , 'address' , 'locality' , 'region' ,
'country-name' , 'gender' , 'pub_keywords' , 'xmpp' ];
2018-07-20 08:19:26 -04:00
$profile = DBA :: selectFirst ( 'profile' , $fields , [ 'uid' => $uid , 'is-default' => true ]);
2018-03-24 02:15:18 -04:00
if ( ! DBM :: is_result ( $profile )) {
return ;
}
$fields = [ 'name' => $profile [ 'name' ], 'nick' => $user [ 'nickname' ],
2018-03-25 04:15:22 -04:00
'avatar-date' => $self [ 'avatar-date' ], 'location' => Profile :: formatLocation ( $profile ),
2018-03-24 02:15:18 -04:00
'about' => $profile [ 'about' ], 'keywords' => $profile [ 'pub_keywords' ],
'gender' => $profile [ 'gender' ], 'avatar' => $profile [ 'photo' ],
'contact-type' => $user [ 'account-type' ], 'xmpp' => $profile [ 'xmpp' ]];
2018-07-20 08:19:26 -04:00
$avatar = DBA :: selectFirst ( 'photo' , [ 'resource-id' , 'type' ], [ 'uid' => $uid , 'profile' => true ]);
2018-03-25 13:26:40 -04:00
if ( DBM :: is_result ( $avatar )) {
if ( $update_avatar ) {
$fields [ 'avatar-date' ] = DateTimeFormat :: utcNow ();
}
2018-03-25 04:15:22 -04:00
2018-03-25 13:26:40 -04:00
// Creating the path to the avatar, beginning with the file suffix
$types = Image :: supportedTypes ();
if ( isset ( $types [ $avatar [ 'type' ]])) {
$file_suffix = $types [ $avatar [ 'type' ]];
} else {
$file_suffix = 'jpg' ;
}
2018-03-25 04:15:22 -04:00
2018-03-25 13:26:40 -04:00
// We are adding a timestamp value so that other systems won't use cached content
$timestamp = strtotime ( $fields [ 'avatar-date' ]);
2018-03-25 04:15:22 -04:00
2018-03-25 13:26:40 -04:00
$prefix = System :: baseUrl () . '/photo/' . $avatar [ 'resource-id' ] . '-' ;
$suffix = '.' . $file_suffix . '?ts=' . $timestamp ;
2018-03-25 04:15:22 -04:00
2018-03-25 13:26:40 -04: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
$fields [ 'photo' ] = System :: baseUrl () . '/images/person-175.jpg' ;
$fields [ 'thumb' ] = System :: baseUrl () . '/images/person-80.jpg' ;
$fields [ 'micro' ] = System :: baseUrl () . '/images/person-48.jpg' ;
}
2018-03-24 02:15:18 -04:00
$fields [ 'forum' ] = $user [ 'page-flags' ] == PAGE_COMMUNITY ;
$fields [ 'prv' ] = $user [ 'page-flags' ] == PAGE_PRVGROUP ;
2018-05-18 08:26:10 -04:00
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
$fields [ 'url' ] = System :: baseUrl () . '/profile/' . $user [ 'nickname' ];
$fields [ 'nurl' ] = normalise_link ( $fields [ 'url' ]);
$fields [ 'addr' ] = $user [ 'nickname' ] . '@' . substr ( System :: baseUrl (), strpos ( System :: baseUrl (), '://' ) + 3 );
$fields [ 'request' ] = System :: baseUrl () . '/dfrn_request/' . $user [ 'nickname' ];
$fields [ 'notify' ] = System :: baseUrl () . '/dfrn_notify/' . $user [ 'nickname' ];
$fields [ 'poll' ] = System :: baseUrl () . '/dfrn_poll/' . $user [ 'nickname' ];
$fields [ 'confirm' ] = System :: baseUrl () . '/dfrn_confirm/' . $user [ 'nickname' ];
$fields [ 'poco' ] = System :: baseUrl () . '/poco/' . $user [ 'nickname' ];
2018-03-24 02:15:18 -04:00
$update = false ;
foreach ( $fields as $field => $content ) {
2018-07-22 16:01:14 -04:00
if ( isset ( $self [ $field ]) && $self [ $field ] != $content ) {
2018-03-24 02:15:18 -04:00
$update = true ;
}
}
if ( $update ) {
$fields [ 'name-date' ] = DateTimeFormat :: utcNow ();
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'id' => $self [ 'id' ]]);
2018-03-25 04:20:13 -04:00
// Update the public contact as well
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'uid' => 0 , 'nurl' => $self [ 'nurl' ]]);
2018-04-14 04:03:15 -04:00
// Update the profile
$fields = [ 'photo' => System :: baseUrl () . '/photo/profile/' . $uid . '.jpg' ,
'thumb' => System :: baseUrl () . '/photo/avatar/' . $uid . '.jpg' ];
2018-07-20 08:19:26 -04:00
DBA :: update ( 'profile' , $fields , [ 'uid' => $uid , 'is-default' => true ]);
2018-03-24 02:15:18 -04:00
}
}
2017-11-19 16:55:28 -05:00
/**
* @ brief Marks a contact for removal
*
2017-11-22 09:02:55 -05:00
* @ param int $id contact id
2017-11-19 16:55:28 -05:00
* @ return null
*/
public static function remove ( $id )
{
// We want just to make sure that we don't delete our "self" contact
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'uid' ], [ 'id' => $id , 'self' => false ]);
2018-01-11 03:26:30 -05:00
if ( ! DBM :: is_result ( $contact ) || ! intval ( $contact [ 'uid' ])) {
2017-11-19 16:55:28 -05:00
return ;
}
2018-01-11 03:26:30 -05:00
$archive = PConfig :: get ( $contact [ 'uid' ], 'system' , 'archive_removed_contacts' );
2017-11-19 16:55:28 -05:00
if ( $archive ) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'archive' => true , 'network' => 'none' , 'writable' => false ], [ 'id' => $id ]);
2017-11-19 16:55:28 -05:00
return ;
}
2018-07-20 08:19:26 -04:00
DBA :: delete ( 'contact' , [ 'id' => $id ]);
2017-11-19 16:55:28 -05:00
// Delete the rest in the background
Worker :: add ( PRIORITY_LOW , 'RemoveContact' , $id );
}
/**
* @ brief Sends an unfriend message . Does not remove the contact
*
2017-11-22 09:02:55 -05:00
* @ param array $user User unfriending
2017-11-19 16:55:28 -05:00
* @ param array $contact Contact unfriended
2017-11-22 23:13:12 -05:00
* @ return void
2017-11-19 16:55:28 -05:00
*/
public static function terminateFriendship ( array $user , array $contact )
{
2018-03-16 02:43:10 -04:00
if ( in_array ( $contact [ 'network' ], [ NETWORK_OSTATUS , NETWORK_DFRN ])) {
2017-11-19 16:55:28 -05:00
// create an unfollow slap
2018-01-15 08:05:12 -05:00
$item = [];
2017-11-19 16:55:28 -05:00
$item [ 'verb' ] = NAMESPACE_OSTATUS . " /unfollow " ;
$item [ 'follow' ] = $contact [ " url " ];
$slap = OStatus :: salmon ( $item , $user );
2018-03-16 02:43:10 -04:00
if ( ! empty ( $contact [ 'notify' ])) {
2017-12-02 09:32:45 -05:00
Salmon :: slapper ( $user , $contact [ 'notify' ], $slap );
2017-11-19 16:55:28 -05:00
}
2018-03-16 02:43:10 -04:00
} elseif ( $contact [ 'network' ] == NETWORK_DIASPORA ) {
2017-11-19 16:55:28 -05:00
Diaspora :: sendUnshare ( $user , $contact );
}
}
/**
* @ brief Marks a contact for archival after a communication issue delay
*
* 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 09:02:55 -05:00
* @ param array $contact contact to mark for archival
2017-12-17 15:27:50 -05:00
* @ return null
2017-11-19 16:55:28 -05:00
*/
public static function markForArchival ( array $contact )
{
2017-12-05 16:29:33 -05:00
// Contact already archived or "self" contact? => nothing to do
if ( $contact [ 'archive' ] || $contact [ 'self' ]) {
2017-11-19 16:55:28 -05:00
return ;
}
2017-12-04 14:37:36 -05:00
2017-11-19 16:55:28 -05:00
if ( $contact [ 'term-date' ] <= NULL_DATE ) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'term-date' => DateTimeFormat :: utcNow ()], [ 'id' => $contact [ 'id' ]]);
2017-11-19 16:55:28 -05:00
if ( $contact [ 'url' ] != '' ) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'term-date' => DateTimeFormat :: utcNow ()], [ '`nurl` = ? AND `term-date` <= ? AND NOT `self`' , normalise_link ( $contact [ 'url' ]), NULL_DATE ]);
2017-11-19 16:55:28 -05: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
2018-04-10 07:10:02 -04:00
$archival_days = Config :: get ( 'system' , 'archival_days' , 32 );
$expiry = $contact [ 'term-date' ] . ' + ' . $archival_days . ' days ' ;
2018-01-26 21:38:34 -05:00
if ( DateTimeFormat :: utcNow () > DateTimeFormat :: utc ( $expiry )) {
2017-11-19 16:55:28 -05: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 .
*/
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'archive' => 1 ], [ 'id' => $contact [ 'id' ]]);
2017-11-19 16:55:28 -05:00
if ( $contact [ 'url' ] != '' ) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'archive' => 1 ], [ 'nurl' => normalise_link ( $contact [ 'url' ]), 'self' => false ]);
2017-11-19 16:55:28 -05:00
}
}
}
}
/**
* @ brief Cancels the archival countdown
*
* @ see Contact :: markForArchival ()
*
2017-11-22 09:02:55 -05:00
* @ param array $contact contact to be unmarked for archival
2017-11-19 16:55:28 -05:00
* @ return null
*/
public static function unmarkForArchival ( array $contact )
{
2018-01-15 08:05:12 -05:00
$condition = [ '`id` = ? AND (`term-date` > ? OR `archive`)' , $contact [ 'id' ], NULL_DATE ];
2018-07-20 08:19:26 -04:00
$exists = DBA :: exists ( 'contact' , $condition );
2017-11-19 16:55:28 -05:00
// We don't need to update, we never marked this contact for archival
2017-11-23 08:15:38 -05:00
if ( ! $exists ) {
2017-11-19 16:55:28 -05:00
return ;
}
// It's a miracle. Our dead contact has inexplicably come back to life.
2018-01-15 08:05:12 -05:00
$fields = [ 'term-date' => NULL_DATE , 'archive' => false ];
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2017-11-19 16:55:28 -05:00
2018-04-11 14:56:22 -04:00
if ( ! empty ( $contact [ 'url' ])) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'nurl' => normalise_link ( $contact [ 'url' ])]);
2017-11-19 16:55:28 -05:00
}
2018-04-11 14:56:22 -04:00
if ( ! empty ( $contact [ 'batch' ])) {
$condition = [ 'batch' => $contact [ 'batch' ], 'contact-type' => ACCOUNT_TYPE_RELAY ];
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , $condition );
2018-04-11 14:56:22 -04:00
}
2017-11-19 16:55:28 -05:00
}
/**
* @ brief Get contact data for a given profile link
*
* 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 09:02:55 -05: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 16:55:28 -05:00
*
* @ return array Contact data
*/
public static function getDetailsByURL ( $url , $uid = - 1 , array $default = [])
{
2018-01-15 08:05:12 -05:00
static $cache = [];
2017-11-19 16:55:28 -05: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 );
// Fetch contact data from the contact table for the given user
2018-07-20 08:19:26 -04: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`,
2017-11-19 16:55:28 -05:00
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self`
FROM `contact` WHERE `nurl` = ? AND `uid` = ? " , normalise_link( $url ), $uid );
2018-07-20 22:03:40 -04:00
$r = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
// Fetch contact data from the contact table for the given user, checking with the alias
if ( ! DBM :: is_result ( $r )) {
2018-07-20 08:19:26 -04: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`,
2017-11-19 16:55:28 -05:00
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self`
FROM `contact` WHERE `alias` IN ( ? , ? , ? ) AND `uid` = ? " , normalise_link( $url ), $url , $ssl_url , $uid );
2018-07-20 22:03:40 -04:00
$r = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
if ( ! DBM :: is_result ( $r )) {
2018-07-20 08:19:26 -04: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`,
2017-11-19 16:55:28 -05:00
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self`
FROM `contact` WHERE `nurl` = ? AND `uid` = 0 " , normalise_link( $url ));
2018-07-20 22:03:40 -04:00
$r = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias
if ( ! DBM :: is_result ( $r )) {
2018-07-20 08:19:26 -04: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`,
2017-11-19 16:55:28 -05:00
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self`
FROM `contact` WHERE `alias` IN ( ? , ? , ? ) AND `uid` = 0 " , normalise_link( $url ), $url , $ssl_url );
2018-07-20 22:03:40 -04:00
$r = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
}
// Fetch the data from the gcontact table
if ( ! DBM :: is_result ( $r )) {
2018-07-20 08:19:26 -04: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`,
2018-04-21 17:59:02 -04:00
`keywords` , `gender` , `photo` , `photo` AS `thumb` , `photo` AS `micro` , 0 AS `forum` , 0 AS `prv` , `community` , `contact-type` , `birthday` , 0 AS `self`
2017-11-19 16:55:28 -05:00
FROM `gcontact` WHERE `nurl` = ? " , normalise_link( $url ));
2018-07-20 22:03:40 -04:00
$r = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
}
if ( DBM :: is_result ( $r )) {
// If there is more than one entry we filter out the connector networks
if ( count ( $r ) > 1 ) {
2017-11-22 09:02:55 -05:00
foreach ( $r as $id => $result ) {
2017-11-19 16:55:28 -05:00
if ( $result [ " network " ] == NETWORK_STATUSNET ) {
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.
if ( $profile [ " birthday " ] > '0001-01-01' ) {
$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 {
$profile [ " bd " ] = '0001-01-01' ;
}
} else {
$profile = $default ;
}
2018-07-10 08:27:56 -04:00
if ( empty ( $profile [ " photo " ]) && isset ( $default [ " photo " ])) {
2017-11-19 16:55:28 -05:00
$profile [ " photo " ] = $default [ " photo " ];
}
2018-07-10 08:27:56 -04:00
if ( empty ( $profile [ " name " ]) && isset ( $default [ " name " ])) {
2017-11-19 16:55:28 -05:00
$profile [ " name " ] = $default [ " name " ];
}
2018-07-10 08:27:56 -04:00
if ( empty ( $profile [ " network " ]) && isset ( $default [ " network " ])) {
2017-11-19 16:55:28 -05:00
$profile [ " network " ] = $default [ " network " ];
}
2018-07-10 08:27:56 -04:00
if ( empty ( $profile [ " thumb " ]) && isset ( $profile [ " photo " ])) {
2017-11-19 16:55:28 -05:00
$profile [ " thumb " ] = $profile [ " photo " ];
}
2018-07-10 08:27:56 -04:00
if ( empty ( $profile [ " micro " ]) && isset ( $profile [ " thumb " ])) {
2017-11-19 16:55:28 -05:00
$profile [ " micro " ] = $profile [ " thumb " ];
}
2018-07-10 08:27:56 -04:00
if (( empty ( $profile [ " addr " ]) || empty ( $profile [ " name " ])) && ( defaults ( $profile , " gid " , 0 ) != 0 )
2018-01-15 08:05:12 -05:00
&& in_array ( $profile [ " network " ], [ NETWORK_DFRN , NETWORK_DIASPORA , NETWORK_OSTATUS ])
2017-11-22 09:02:55 -05:00
) {
2017-11-19 16:55:28 -05:00
Worker :: add ( PRIORITY_LOW , " UpdateGContact " , $profile [ " gid " ]);
}
// Show contact details of Diaspora contacts only if connected
2018-07-10 08:27:56 -04:00
if (( defaults ( $profile , " cid " , 0 ) == 0 ) && ( defaults ( $profile , " network " , " " ) == NETWORK_DIASPORA )) {
2017-11-19 16:55:28 -05:00
$profile [ " location " ] = " " ;
$profile [ " about " ] = " " ;
$profile [ " gender " ] = " " ;
$profile [ " birthday " ] = '0001-01-01' ;
}
$cache [ $url ][ $uid ] = $profile ;
return $profile ;
}
/**
* @ brief Get contact data for a given address
*
* The function looks at several places ( contact table and gcontact table ) for the contact
*
* @ param string $addr The profile link
2017-11-22 09:02:55 -05:00
* @ param int $uid User id
2017-11-19 16:55:28 -05:00
*
* @ return array Contact data
*/
public static function getDetailsByAddr ( $addr , $uid = - 1 )
{
2018-01-15 08:05:12 -05:00
static $cache = [];
2017-11-19 16:55:28 -05:00
if ( $addr == '' ) {
2018-01-15 08:05:12 -05:00
return [];
2017-11-19 16:55:28 -05: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`,
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , `self`
2018-01-11 03:37:11 -05:00
FROM `contact` WHERE `addr` = '%s' AND `uid` = % d " ,
dbesc ( $addr ),
intval ( $uid )
);
2017-11-19 16:55:28 -05:00
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
2018-01-11 03:37:11 -05:00
if ( ! DBM :: is_result ( $r )) {
2017-11-19 16:55:28 -05: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`,
2018-01-11 03:37:11 -05:00
`keywords` , `gender` , `photo` , `thumb` , `micro` , `forum` , `prv` , ( `forum` | `prv` ) AS `community` , `contact-type` , `bd` AS `birthday` , 0 AS `self`
FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 " ,
dbesc ( $addr )
);
}
2017-11-19 16:55:28 -05:00
// Fetch the data from the gcontact table
2018-01-11 03:37:11 -05:00
if ( ! DBM :: is_result ( $r )) {
2017-11-19 16:55:28 -05: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`,
2018-01-11 03:37:11 -05:00
`keywords` , `gender` , `photo` , `photo` AS `thumb` , `photo` AS `micro` , `community` AS `forum` , 0 AS `prv` , `community` , `contact-type` , `birthday` , 0 AS `self`
FROM `gcontact` WHERE `addr` = '%s' " ,
dbesc ( $addr )
);
}
2017-11-19 16:55:28 -05:00
if ( ! DBM :: is_result ( $r )) {
$data = Probe :: uri ( $addr );
$profile = self :: getDetailsByURL ( $data [ 'url' ], $uid );
} else {
$profile = $r [ 0 ];
}
return $profile ;
}
/**
* @ brief Returns the data array for the photo menu of a given contact
*
2017-11-22 09:02:55 -05:00
* @ param array $contact contact
* @ param int $uid optional , default 0
2017-11-19 16:55:28 -05:00
* @ return array
*/
public static function photoMenu ( array $contact , $uid = 0 )
{
// @todo Unused, to be removed
$a = get_app ();
$contact_url = '' ;
$pm_url = '' ;
$status_link = '' ;
$photos_link = '' ;
$posts_link = '' ;
$contact_drop_link = '' ;
$poke_link = '' ;
if ( $uid == 0 ) {
$uid = local_user ();
}
2018-07-10 08:27:56 -04:00
if ( empty ( $contact [ 'uid' ]) || ( $contact [ 'uid' ] != $uid )) {
2017-11-19 16:55:28 -05:00
if ( $uid == 0 ) {
2018-06-02 04:05:06 -04:00
$profile_link = self :: magicLink ( $contact [ 'url' ]);
2018-01-22 09:54:13 -05:00
$menu = [ 'profile' => [ L10n :: t ( 'View Profile' ), $profile_link , true ]];
2017-11-19 16:55:28 -05:00
return $menu ;
}
2018-01-11 03:26:30 -05:00
// Look for our own contact if the uid doesn't match and isn't public
2018-07-20 08:19:26 -04:00
$contact_own = DBA :: selectFirst ( 'contact' , [], [ 'nurl' => $contact [ 'nurl' ], 'network' => $contact [ 'network' ], 'uid' => $uid ]);
2018-01-11 03:26:30 -05:00
if ( DBM :: is_result ( $contact_own )) {
return self :: photoMenu ( $contact_own , $uid );
2017-11-19 16:55:28 -05:00
} else {
2018-06-02 04:05:06 -04:00
$profile_link = self :: magicLink ( $contact [ 'url' ]);
2017-11-19 16:55:28 -05:00
$connlnk = 'follow/?url=' . $contact [ 'url' ];
2018-01-15 08:05:12 -05:00
$menu = [
2018-01-22 09:54:13 -05:00
'profile' => [ L10n :: t ( 'View Profile' ), $profile_link , true ],
'follow' => [ L10n :: t ( 'Connect/Follow' ), $connlnk , true ]
2018-01-15 08:05:12 -05:00
];
2017-11-19 16:55:28 -05:00
return $menu ;
}
}
$sparkle = false ;
2018-03-17 02:17:32 -04:00
if (( $contact [ 'network' ] === NETWORK_DFRN ) && ! $contact [ 'self' ]) {
2017-11-19 16:55:28 -05:00
$sparkle = true ;
$profile_link = System :: baseUrl () . '/redir/' . $contact [ 'id' ];
} else {
$profile_link = $contact [ 'url' ];
}
if ( $profile_link === 'mailbox' ) {
$profile_link = '' ;
}
if ( $sparkle ) {
$status_link = $profile_link . '?url=status' ;
$photos_link = $profile_link . '?url=photos' ;
$profile_link = $profile_link . '?url=profile' ;
}
2018-03-17 02:17:32 -04:00
if ( in_array ( $contact [ 'network' ], [ NETWORK_DFRN , NETWORK_DIASPORA ]) && ! $contact [ 'self' ]) {
2017-11-19 16:55:28 -05:00
$pm_url = System :: baseUrl () . '/message/new/' . $contact [ 'id' ];
}
2018-03-17 02:17:32 -04:00
if (( $contact [ 'network' ] == NETWORK_DFRN ) && ! $contact [ 'self' ]) {
2017-11-19 16:55:28 -05:00
$poke_link = System :: baseUrl () . '/poke/?f=&c=' . $contact [ 'id' ];
}
$contact_url = System :: baseUrl () . '/contacts/' . $contact [ 'id' ];
$posts_link = System :: baseUrl () . '/contacts/' . $contact [ 'id' ] . '/posts' ;
2018-03-17 02:17:32 -04:00
if ( ! $contact [ 'self' ]) {
$contact_drop_link = System :: baseUrl () . '/contacts/' . $contact [ 'id' ] . '/drop?confirm=1' ;
}
2017-11-19 16:55:28 -05:00
/**
2017-11-22 09:02:55 -05:00
* Menu array :
2017-11-19 16:55:28 -05:00
* " name " => [ " Label " , " link " , ( bool ) Should the link opened in a new tab ? ]
*/
2018-01-11 03:37:11 -05:00
$menu = [
2018-01-22 09:54:13 -05:00
'status' => [ L10n :: t ( " View Status " ) , $status_link , true ],
'profile' => [ L10n :: t ( " View Profile " ) , $profile_link , true ],
'photos' => [ L10n :: t ( " View Photos " ) , $photos_link , true ],
'network' => [ L10n :: t ( " Network Posts " ), $posts_link , false ],
'edit' => [ L10n :: t ( " View Contact " ) , $contact_url , false ],
'drop' => [ L10n :: t ( " Drop Contact " ) , $contact_drop_link , false ],
'pm' => [ L10n :: t ( " Send PM " ) , $pm_url , false ],
'poke' => [ L10n :: t ( " Poke " ) , $poke_link , false ],
2018-01-11 03:37:11 -05:00
];
$args = [ 'contact' => $contact , 'menu' => & $menu ];
2017-11-19 16:55:28 -05:00
2018-01-17 13:42:40 -05:00
Addon :: callHooks ( 'contact_photo_menu' , $args );
2017-11-19 16:55:28 -05:00
2018-01-15 08:05:12 -05:00
$menucondensed = [];
2017-11-19 16:55:28 -05:00
2017-11-22 09:02:55 -05:00
foreach ( $menu as $menuname => $menuitem ) {
2017-11-19 16:55:28 -05:00
if ( $menuitem [ 1 ] != '' ) {
$menucondensed [ $menuname ] = $menuitem ;
}
}
return $menucondensed ;
}
/**
2017-11-22 09:02:55 -05:00
* @ brief Returns ungrouped contact count or list for user
*
2017-11-19 16:55:28 -05:00
* Returns either the total number of ungrouped contacts for the given user
* id or a paginated list of ungrouped contacts .
*
2017-11-22 09:02:55 -05:00
* @ param int $uid uid
* @ param int $start optional , default 0
* @ param int $count optional , default 0
2017-11-19 16:55:28 -05:00
*
* @ return array
*/
2018-07-09 18:36:50 -04:00
public static function getUngroupedList ( $uid )
2017-11-19 16:55:28 -05:00
{
2018-07-09 18:36:50 -04:00
return q ( " SELECT *
FROM `contact`
WHERE `uid` = % d
AND NOT `self`
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 16:55:28 -05:00
}
/**
2018-01-11 03:37:11 -05:00
* @ brief Fetch the contact id for a given URL and user
2017-11-19 16:55:28 -05: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 03:37:11 -05:00
* Second , we probe the provided $url whether it ' s http :// server . tld / profile or
2017-11-19 16:55:28 -05: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 09:02:55 -05:00
* @ param string $url Contact URL
* @ param integer $uid The user id for the contact ( 0 = public contact )
2017-11-19 16:55:28 -05:00
* @ param boolean $no_update Don ' t update the contact
2018-05-08 13:50:06 -04:00
* @ param array $default Default value for creating the contact when every else fails
2017-11-19 16:55:28 -05:00
*
* @ return integer Contact ID
*/
2018-05-08 13:50:06 -04:00
public static function getIdForURL ( $url , $uid = 0 , $no_update = false , $default = [])
2017-11-19 16:55:28 -05:00
{
logger ( " Get contact data for url " . $url . " and user " . $uid . " - " . System :: callstack (), LOGGER_DEBUG );
$contact_id = 0 ;
if ( $url == '' ) {
return 0 ;
}
/// @todo Verify if we can't use Contact::getDetailsByUrl instead of the following
// We first try the nurl (http://server.tld/nick), most common case
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'avatar' , 'avatar-date' ], [ 'nurl' => normalise_link ( $url ), 'uid' => $uid ]);
2017-11-19 16:55:28 -05:00
// Then the addr (nick@server.tld)
if ( ! DBM :: is_result ( $contact )) {
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'avatar' , 'avatar-date' ], [ 'addr' => $url , 'uid' => $uid ]);
2017-11-19 16:55:28 -05:00
}
// Then the alias (which could be anything)
if ( ! DBM :: is_result ( $contact )) {
// The link could be provided as http although we stored it as https
$ssl_url = str_replace ( 'http://' , 'https://' , $url );
2018-01-11 03:26:30 -05:00
$condition = [ '`alias` IN (?, ?, ?) AND `uid` = ?' , $url , normalise_link ( $url ), $ssl_url , $uid ];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'avatar' , 'avatar-date' ], $condition );
2017-11-19 16:55:28 -05:00
}
if ( DBM :: is_result ( $contact )) {
$contact_id = $contact [ " id " ];
// Update the contact every 7 days
2018-01-26 21:38:34 -05:00
$update_contact = ( $contact [ 'avatar-date' ] < DateTimeFormat :: utc ( 'now -7 days' ));
2017-11-19 16:55:28 -05:00
// We force the update if the avatar is empty
2018-01-01 15:51:02 -05:00
if ( ! x ( $contact , 'avatar' )) {
2017-11-19 16:55:28 -05:00
$update_contact = true ;
}
if ( ! $update_contact || $no_update ) {
return $contact_id ;
}
} elseif ( $uid != 0 ) {
// Non-existing user-specific contact, exiting
return 0 ;
}
$data = Probe :: uri ( $url , " " , $uid );
// Last try in gcontact for unsupported networks
2018-05-08 02:11:46 -04:00
if ( ! in_array ( $data [ " network " ], [ NETWORK_DFRN , NETWORK_OSTATUS , NETWORK_DIASPORA , NETWORK_PUMPIO , NETWORK_MAIL , NETWORK_FEED ])) {
2017-11-19 16:55:28 -05:00
if ( $uid != 0 ) {
return 0 ;
}
// Get data from the gcontact table
2018-05-08 13:50:06 -04:00
$fields = [ 'name' , 'nick' , 'url' , 'photo' , 'addr' , 'alias' , 'network' ];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'gcontact' , $fields , [ 'nurl' => normalise_link ( $url )]);
2018-05-08 13:50:06 -04:00
if ( ! DBM :: is_result ( $contact )) {
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'nurl' => normalise_link ( $url )]);
2018-05-08 13:50:06 -04:00
}
if ( ! DBM :: is_result ( $contact )) {
$fields = [ 'url' , 'addr' , 'alias' , 'notify' , 'poll' , 'name' , 'nick' ,
'photo' , 'keywords' , 'location' , 'about' , 'network' ,
'priority' , 'batch' , 'request' , 'confirm' , 'poco' ];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'addr' => $url ]);
2018-05-08 13:50:06 -04:00
}
if ( ! DBM :: is_result ( $contact )) {
// The link could be provided as http although we stored it as https
$ssl_url = str_replace ( 'http://' , 'https://' , $url );
$condition = [ 'alias' => [ $url , normalise_link ( $url ), $ssl_url ]];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , $fields , $condition );
2018-05-08 13:50:06 -04:00
}
if ( ! DBM :: is_result ( $contact )) {
$fields = [ 'url' , 'addr' , 'alias' , 'notify' , 'poll' , 'name' , 'nick' ,
'photo' , 'network' , 'priority' , 'batch' , 'request' , 'confirm' ];
$condition = [ 'url' => [ $url , normalise_link ( $url ), $ssl_url ]];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'fcontact' , $fields , $condition );
2018-05-08 13:50:06 -04:00
}
if ( ! empty ( $default )) {
$contact = $default ;
2017-11-19 16:55:28 -05:00
}
2018-05-08 13:50:06 -04:00
if ( ! DBM :: is_result ( $contact )) {
return 0 ;
} else {
$data = array_merge ( $data , $contact );
}
2017-11-19 16:55:28 -05:00
}
if ( ! $contact_id && ( $data [ " alias " ] != '' ) && ( $data [ " alias " ] != $url )) {
2017-11-19 17:03:39 -05:00
$contact_id = self :: getIdForURL ( $data [ " alias " ], $uid , true );
2017-11-19 16:55:28 -05:00
}
$url = $data [ " url " ];
if ( ! $contact_id ) {
2018-07-20 08:19:26 -04:00
DBA :: insert ( 'contact' , [
2018-01-11 03:37:11 -05:00
'uid' => $uid ,
2018-01-26 21:38:34 -05:00
'created' => DateTimeFormat :: utcNow (),
2018-01-11 03:37:11 -05:00
'url' => $data [ " url " ],
'nurl' => normalise_link ( $data [ " url " ]),
'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 " ],
'network' => $data [ " network " ],
'pubkey' => $data [ " pubkey " ],
'rel' => CONTACT_IS_SHARING ,
'priority' => $data [ " priority " ],
'batch' => $data [ " batch " ],
'request' => $data [ " request " ],
'confirm' => $data [ " confirm " ],
'poco' => $data [ " poco " ],
2018-01-26 21:38:34 -05:00
'name-date' => DateTimeFormat :: utcNow (),
'uri-date' => DateTimeFormat :: utcNow (),
'avatar-date' => DateTimeFormat :: utcNow (),
2018-01-11 03:37:11 -05:00
'writable' => 1 ,
'blocked' => 0 ,
'readonly' => 0 ,
'pending' => 0 ]
2017-11-22 16:29:47 -05:00
);
2017-11-19 16:55:28 -05:00
2018-07-20 08:19:26 -04:00
$s = DBA :: select ( 'contact' , [ 'id' ], [ 'nurl' => normalise_link ( $data [ " url " ]), 'uid' => $uid ], [ 'order' => [ 'id' ], 'limit' => 2 ]);
2018-07-20 22:03:40 -04:00
$contacts = DBA :: toArray ( $s );
2017-11-19 16:55:28 -05:00
if ( ! DBM :: is_result ( $contacts )) {
return 0 ;
}
$contact_id = $contacts [ 0 ][ " id " ];
// Update the newly created contact from data in the gcontact table
2018-07-20 08:19:26 -04:00
$gcontact = DBA :: selectFirst ( 'gcontact' , [ 'location' , 'about' , 'keywords' , 'gender' ], [ 'nurl' => normalise_link ( $data [ " url " ])]);
2017-11-19 16:55:28 -05:00
if ( DBM :: is_result ( $gcontact )) {
// Only use the information when the probing hadn't fetched these values
if ( $data [ 'keywords' ] != '' ) {
unset ( $gcontact [ 'keywords' ]);
}
if ( $data [ 'location' ] != '' ) {
unset ( $gcontact [ 'location' ]);
}
if ( $data [ 'about' ] != '' ) {
unset ( $gcontact [ 'about' ]);
}
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $gcontact , [ 'id' => $contact_id ]);
2017-11-19 16:55:28 -05:00
}
if ( count ( $contacts ) > 1 && $uid == 0 && $contact_id != 0 && $data [ " url " ] != " " ) {
2018-07-20 08:19:26 -04:00
DBA :: delete ( 'contact' , [ " `nurl` = ? AND `uid` = 0 AND `id` != ? AND NOT `self` " ,
2018-01-11 03:37:11 -05:00
normalise_link ( $data [ " url " ]), $contact_id ]);
2017-11-19 16:55:28 -05:00
}
}
2017-11-29 17:29:11 -05:00
self :: updateAvatar ( $data [ " photo " ], $uid , $contact_id );
2017-11-19 16:55:28 -05:00
2018-01-09 22:20:33 -05:00
$fields = [ 'url' , 'nurl' , 'addr' , 'alias' , 'name' , 'nick' , 'keywords' , 'location' , 'about' , 'avatar-date' , 'pubkey' ];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $contact_id ]);
2017-11-19 16:55:28 -05:00
// This condition should always be true
if ( ! DBM :: is_result ( $contact )) {
return $contact_id ;
}
2018-01-15 08:05:12 -05:00
$updated = [ 'addr' => $data [ 'addr' ],
2017-11-19 16:55:28 -05:00
'alias' => $data [ 'alias' ],
'url' => $data [ 'url' ],
'nurl' => normalise_link ( $data [ 'url' ]),
'name' => $data [ 'name' ],
2018-01-15 08:05:12 -05:00
'nick' => $data [ 'nick' ]];
2017-11-19 16:55:28 -05:00
if ( $data [ 'keywords' ] != '' ) {
$updated [ 'keywords' ] = $data [ 'keywords' ];
}
if ( $data [ 'location' ] != '' ) {
$updated [ 'location' ] = $data [ 'location' ];
}
2018-06-03 04:36:05 -04:00
// Update the technical stuff as well - if filled
if ( $data [ 'notify' ] != '' ) {
$updated [ 'notify' ] = $data [ 'notify' ];
}
if ( $data [ 'poll' ] != '' ) {
$updated [ 'poll' ] = $data [ 'poll' ];
}
if ( $data [ 'batch' ] != '' ) {
$updated [ 'batch' ] = $data [ 'batch' ];
}
if ( $data [ 'request' ] != '' ) {
$updated [ 'request' ] = $data [ 'request' ];
}
if ( $data [ 'confirm' ] != '' ) {
$updated [ 'confirm' ] = $data [ 'confirm' ];
}
if ( $data [ 'poco' ] != '' ) {
$updated [ 'poco' ] = $data [ 'poco' ];
}
// Only fill the pubkey if it had been empty before. We have to prevent identity theft.
if ( empty ( $contact [ 'pubkey' ])) {
$updated [ 'pubkey' ] = $data [ 'pubkey' ];
2017-11-19 16:55:28 -05:00
}
if (( $data [ " addr " ] != $contact [ " addr " ]) || ( $data [ " alias " ] != $contact [ " alias " ])) {
2018-01-26 21:38:34 -05:00
$updated [ 'uri-date' ] = DateTimeFormat :: utcNow ();
2017-11-19 16:55:28 -05:00
}
if (( $data [ " name " ] != $contact [ " name " ]) || ( $data [ " nick " ] != $contact [ " nick " ])) {
2018-01-26 21:38:34 -05:00
$updated [ 'name-date' ] = DateTimeFormat :: utcNow ();
2017-11-19 16:55:28 -05:00
}
2018-01-26 21:38:34 -05:00
$updated [ 'avatar-date' ] = DateTimeFormat :: utcNow ();
2017-11-19 16:55:28 -05:00
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $updated , [ 'id' => $contact_id ], $contact );
2017-11-19 16:55:28 -05:00
return $contact_id ;
}
/**
* @ brief Checks if the contact is blocked
*
* @ param int $cid contact id
*
* @ return boolean Is the contact blocked ?
*/
public static function isBlocked ( $cid )
{
if ( $cid == 0 ) {
return false ;
}
2018-07-20 08:19:26 -04:00
$blocked = DBA :: selectFirst ( 'contact' , [ 'blocked' ], [ 'id' => $cid ]);
2017-11-19 16:55:28 -05:00
if ( ! DBM :: is_result ( $blocked )) {
return false ;
}
return ( bool ) $blocked [ 'blocked' ];
}
/**
* @ brief Checks if the contact is hidden
*
* @ param int $cid contact id
*
* @ return boolean Is the contact hidden ?
*/
public static function isHidden ( $cid )
{
if ( $cid == 0 ) {
return false ;
}
2018-07-20 08:19:26 -04:00
$hidden = DBA :: selectFirst ( 'contact' , [ 'hidden' ], [ 'id' => $cid ]);
2017-11-19 16:55:28 -05:00
if ( ! DBM :: is_result ( $hidden )) {
return false ;
}
return ( bool ) $hidden [ 'hidden' ];
}
/**
* @ brief Returns posts from a given contact url
*
* @ param string $contact_url Contact URL
*
* @ return string posts in HTML
*/
public static function getPostsFromUrl ( $contact_url )
{
2017-11-24 17:15:35 -05:00
$a = self :: getApp ();
2017-11-19 16:55:28 -05:00
require_once 'include/conversation.php' ;
// There are no posts with "uid = 0" with connector networks
// This speeds up the query a lot
2017-11-23 08:15:38 -05:00
$r = q ( " SELECT `network`, `id` AS `author-id`, `contact-type` FROM `contact`
2018-01-11 03:37:11 -05:00
WHERE `contact` . `nurl` = '%s' AND `contact` . `uid` = 0 " ,
dbesc ( normalise_link ( $contact_url ))
);
2017-11-19 16:55:28 -05:00
if ( ! DBM :: is_result ( $r )) {
return '' ;
}
2018-01-15 08:05:12 -05:00
if ( in_array ( $r [ 0 ][ " network " ], [ NETWORK_DFRN , NETWORK_DIASPORA , NETWORK_OSTATUS , " " ])) {
2018-06-10 06:14:53 -04:00
$sql = " (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) " ;
2017-11-19 16:55:28 -05:00
} else {
2018-06-10 06:14:53 -04:00
$sql = " `item`.`uid` = ? " ;
2017-11-19 16:55:28 -05:00
}
2017-11-23 08:15:38 -05:00
$author_id = intval ( $r [ 0 ][ " author-id " ]);
2017-11-19 16:55:28 -05:00
2017-11-23 08:15:38 -05:00
$contact = ( $r [ 0 ][ " contact-type " ] == ACCOUNT_TYPE_COMMUNITY ? 'owner-id' : 'author-id' );
2017-11-19 16:55:28 -05:00
2018-06-27 14:09:33 -04:00
$condition = [ " ` $contact ` = ? AND `gravity` IN (?, ?) AND " . $sql ,
$author_id , GRAVITY_PARENT , GRAVITY_COMMENT , local_user ()];
2018-06-10 06:14:53 -04:00
$params = [ 'order' => [ 'created' => true ],
'limit' => [ $a -> pager [ 'start' ], $a -> pager [ 'itemspage' ]]];
2018-06-17 13:05:17 -04:00
$r = Item :: selectForUser ( local_user (), [], $condition , $params );
2017-11-19 16:55:28 -05:00
2018-06-24 06:48:29 -04:00
$items = Item :: inArray ( $r );
2018-06-17 02:27:52 -04:00
2018-06-15 20:50:31 -04:00
$o = conversation ( $a , $items , 'contact-posts' , false );
2017-11-19 16:55:28 -05:00
2018-06-15 20:50:31 -04:00
$o .= alt_pager ( $a , count ( $items ));
2017-11-19 16:55:28 -05:00
return $o ;
}
/**
* @ brief Returns the account type name
*
* 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,
// "forum" and "prv" are used in the contact table. They stand for PAGE_COMMUNITY and PAGE_PRVGROUP.
// "community" is used in the gcontact table and is true if the contact is PAGE_COMMUNITY or PAGE_PRVGROUP.
2017-11-22 16:29:47 -05:00
if (( isset ( $contact [ 'page-flags' ]) && ( intval ( $contact [ 'page-flags' ]) == PAGE_COMMUNITY ))
2017-11-19 16:55:28 -05:00
|| ( isset ( $contact [ 'page-flags' ]) && ( intval ( $contact [ 'page-flags' ]) == PAGE_PRVGROUP ))
|| ( isset ( $contact [ 'forum' ]) && intval ( $contact [ 'forum' ]))
|| ( isset ( $contact [ 'prv' ]) && intval ( $contact [ 'prv' ]))
|| ( isset ( $contact [ 'community' ]) && intval ( $contact [ 'community' ]))
) {
$type = ACCOUNT_TYPE_COMMUNITY ;
} else {
$type = ACCOUNT_TYPE_PERSON ;
}
// 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 03:37:11 -05:00
2017-11-19 16:55:28 -05:00
if ( isset ( $contact [ " account-type " ])) {
$type = $contact [ " account-type " ];
}
switch ( $type ) {
case ACCOUNT_TYPE_ORGANISATION :
2018-01-22 09:54:13 -05:00
$account_type = L10n :: t ( " Organisation " );
2017-11-19 16:55:28 -05:00
break ;
case ACCOUNT_TYPE_NEWS :
2018-01-22 09:54:13 -05:00
$account_type = L10n :: t ( 'News' );
2017-11-19 16:55:28 -05:00
break ;
case ACCOUNT_TYPE_COMMUNITY :
2018-01-22 09:54:13 -05:00
$account_type = L10n :: t ( " Forum " );
2017-11-19 16:55:28 -05:00
break ;
default :
$account_type = " " ;
break ;
}
return $account_type ;
}
2017-11-29 17:29:11 -05:00
/**
2017-12-01 00:41:26 -05:00
* @ brief Blocks a contact
*
* @ param int $uid
* @ return bool
*/
public static function block ( $uid )
{
2018-07-20 08:19:26 -04:00
$return = DBA :: update ( 'contact' , [ 'blocked' => true ], [ 'id' => $uid ]);
2017-12-01 00:41:26 -05:00
return $return ;
}
/**
* @ brief Unblocks a contact
*
* @ param int $uid
* @ return bool
*/
public static function unblock ( $uid )
{
2018-07-20 08:19:26 -04:00
$return = DBA :: update ( 'contact' , [ 'blocked' => false ], [ 'id' => $uid ]);
2017-12-01 00:41:26 -05:00
return $return ;
2018-01-11 03:37:11 -05:00
}
2017-12-01 00:48:13 -05:00
2018-01-11 03:37:11 -05:00
/**
* @ brief Updates the avatar links in a contact only if needed
2017-11-29 17:29:11 -05: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
*
* @ return array Returns array of the different avatar sizes
*/
public static function updateAvatar ( $avatar , $uid , $cid , $force = false )
{
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'avatar' , 'photo' , 'thumb' , 'micro' , 'nurl' ], [ 'id' => $cid ]);
2018-01-11 03:26:30 -05:00
if ( ! DBM :: is_result ( $contact )) {
2017-11-29 17:29:11 -05:00
return false ;
} else {
2018-01-15 08:05:12 -05:00
$data = [ $contact [ " photo " ], $contact [ " thumb " ], $contact [ " micro " ]];
2017-11-29 17:29:11 -05:00
}
2018-01-11 03:26:30 -05:00
if (( $contact [ " avatar " ] != $avatar ) || $force ) {
2017-12-08 01:15:06 -05:00
$photos = Photo :: importProfilePhoto ( $avatar , $uid , $cid , true );
2017-11-29 17:29:11 -05:00
if ( $photos ) {
2018-07-20 08:19:26 -04:00
DBA :: update (
2017-11-29 17:29:11 -05:00
'contact' ,
2018-01-26 21:38:34 -05:00
[ 'avatar' => $avatar , 'photo' => $photos [ 0 ], 'thumb' => $photos [ 1 ], 'micro' => $photos [ 2 ], 'avatar-date' => DateTimeFormat :: utcNow ()],
2018-01-15 08:05:12 -05:00
[ 'id' => $cid ]
2017-11-29 17:29:11 -05:00
);
// Update the public contact (contact id = 0)
if ( $uid != 0 ) {
2018-07-20 08:19:26 -04:00
$pcontact = DBA :: selectFirst ( 'contact' , [ 'id' ], [ 'nurl' => $contact [ 'nurl' ], 'uid' => 0 ]);
2017-11-29 17:29:11 -05:00
if ( DBM :: is_result ( $pcontact )) {
self :: updateAvatar ( $avatar , 0 , $pcontact [ 'id' ], $force );
}
}
return $photos ;
}
}
return $data ;
}
2018-01-09 09:40:45 -05:00
/**
* @ param integer $id contact id
* @ return boolean
*/
2018-01-09 11:23:26 -05:00
public static function updateFromProbe ( $id )
2018-01-09 09:40:45 -05:00
{
/*
2018-01-11 03:37:11 -05:00
Warning : Never ever fetch the public key via Probe :: uri and write it into the contacts .
This will reliably kill your communication with Friendica contacts .
*/
2018-01-09 09:40:45 -05:00
2018-01-11 03:26:30 -05:00
$fields = [ 'url' , 'nurl' , 'addr' , 'alias' , 'batch' , 'notify' , 'poll' , 'poco' , 'network' ];
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , $fields , [ 'id' => $id ]);
2018-01-11 03:26:30 -05:00
if ( ! DBM :: is_result ( $contact )) {
2018-01-09 09:40:45 -05:00
return false ;
}
2018-01-11 03:26:30 -05:00
$ret = Probe :: uri ( $contact [ " url " ]);
2018-01-09 09:40:45 -05:00
// If Probe::uri fails the network code will be different
2018-01-11 03:26:30 -05:00
if ( $ret [ " network " ] != $contact [ " network " ]) {
2018-01-09 09:40:45 -05:00
return false ;
}
$update = false ;
// make sure to not overwrite existing values with blank entries
2018-01-09 09:44:22 -05:00
foreach ( $ret as $key => $val ) {
2018-01-11 03:26:30 -05:00
if ( isset ( $contact [ $key ]) && ( $contact [ $key ] != " " ) && ( $val == " " )) {
$ret [ $key ] = $contact [ $key ];
}
2018-01-09 09:40:45 -05:00
2018-01-11 03:26:30 -05:00
if ( isset ( $contact [ $key ]) && ( $ret [ $key ] != $contact [ $key ])) {
2018-01-09 09:40:45 -05:00
$update = true ;
2018-01-11 03:26:30 -05:00
}
2018-01-09 09:40:45 -05:00
}
if ( ! $update ) {
return true ;
}
2018-07-20 08:19:26 -04:00
DBA :: update (
2018-01-11 03:37:11 -05:00
'contact' , [
'url' => $ret [ 'url' ],
'nurl' => normalise_link ( $ret [ 'url' ]),
'addr' => $ret [ 'addr' ],
'alias' => $ret [ 'alias' ],
'batch' => $ret [ 'batch' ],
2018-01-09 09:44:22 -05:00
'notify' => $ret [ 'notify' ],
2018-01-11 03:37:11 -05:00
'poll' => $ret [ 'poll' ],
'poco' => $ret [ 'poco' ]
2018-01-09 09:44:22 -05:00
],
[ 'id' => $id ]
2018-01-09 09:40:45 -05:00
);
// Update the corresponding gcontact entry
PortableContact :: lastUpdated ( $ret [ " url " ]);
return true ;
}
/**
* Takes a $uid and a url / handle and adds a new contact
* Currently if the contact is DFRN , interactive needs to be true , to redirect to the
* dfrn_request page .
*
2018-01-11 03:37:11 -05:00
* Otherwise this can be used to bulk add StatusNet contacts , Twitter contacts , etc .
2018-01-09 09:40:45 -05:00
*
* Returns an array
* $return [ 'success' ] boolean true if successful
* $return [ 'message' ] error text if success is false .
*
* @ brief Takes a $uid and a url / handle and adds a new contact
* @ param int $uid
* @ param string $url
* @ param bool $interactive
* @ param string $network
* @ return boolean | string
*/
2018-01-09 11:40:25 -05:00
public static function createFromProbe ( $uid , $url , $interactive = false , $network = '' )
2018-01-09 09:40:45 -05:00
{
2018-01-15 08:05:12 -05:00
$result = [ 'cid' => - 1 , 'success' => false , 'message' => '' ];
2018-01-09 09:40:45 -05:00
$a = get_app ();
// remove ajax junk, e.g. Twitter
$url = str_replace ( '/#!/' , '/' , $url );
2018-01-27 11:13:41 -05:00
if ( ! Network :: isUrlAllowed ( $url )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] = L10n :: t ( 'Disallowed profile URL.' );
2018-01-09 09:40:45 -05:00
return $result ;
}
2018-01-27 11:13:41 -05:00
if ( Network :: isUrlBlocked ( $url )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] = L10n :: t ( 'Blocked domain' );
2018-01-09 09:40:45 -05:00
return $result ;
}
if ( ! $url ) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] = L10n :: t ( 'Connect URL missing.' );
2018-01-09 09:40:45 -05:00
return $result ;
}
2018-01-15 08:05:12 -05:00
$arr = [ 'url' => $url , 'contact' => []];
2018-01-09 09:40:45 -05:00
2018-01-17 13:42:40 -05:00
Addon :: callHooks ( 'follow' , $arr );
2018-01-09 09:40:45 -05:00
2018-01-30 13:51:09 -05:00
if ( empty ( $arr )) {
2018-01-30 15:48:12 -05:00
$result [ 'message' ] = L10n :: t ( 'The contact could not be added. Please check the relevant network credentials in your Settings -> Social Networks page.' );
2018-01-30 13:51:09 -05:00
return $result ;
}
2018-01-09 09:40:45 -05:00
if ( x ( $arr [ 'contact' ], 'name' )) {
$ret = $arr [ 'contact' ];
} else {
$ret = Probe :: uri ( $url , $network , $uid , false );
}
if (( $network != '' ) && ( $ret [ 'network' ] != $network )) {
logger ( 'Expected network ' . $network . ' does not match actual network ' . $ret [ 'network' ]);
2018-05-14 21:23:24 -04:00
return $result ;
2018-01-09 09:40:45 -05:00
}
2018-03-16 02:43:10 -04: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
$r = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' AND NOT `pending` LIMIT 1 " ,
intval ( $uid ),
dbesc ( $ret [ 'poll' ]),
dbesc ( normalise_link ( $ret [ 'poll' ])),
dbesc ( $ret [ 'network' ])
);
if ( ! DBM :: is_result ( $r )) {
$r = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' AND NOT `pending` LIMIT 1 " ,
intval ( $uid ),
dbesc ( normalise_link ( $url )),
dbesc ( $ret [ 'network' ])
);
}
if (( $ret [ 'network' ] === NETWORK_DFRN ) && ! DBM :: is_result ( $r )) {
2018-01-09 09:40:45 -05:00
if ( $interactive ) {
2018-06-25 20:39:56 -04:00
if ( strlen ( $a -> urlpath )) {
2018-01-09 09:40:45 -05:00
$myaddr = bin2hex ( System :: baseUrl () . '/profile/' . $a -> user [ 'nickname' ]);
} else {
$myaddr = bin2hex ( $a -> user [ 'nickname' ] . '@' . $a -> get_hostname ());
}
goaway ( $ret [ 'request' ] . " &addr= $myaddr " );
// NOTREACHED
}
2018-03-16 02:43:10 -04:00
} elseif ( Config :: get ( 'system' , 'dfrn_only' ) && ( $ret [ 'network' ] != NETWORK_DFRN )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] = L10n :: t ( 'This site is not configured to allow communications with other networks.' ) . EOL ;
$result [ 'message' ] != L10n :: t ( 'No compatible communication protocols or feeds were discovered.' ) . EOL ;
2018-01-09 09:40:45 -05:00
return $result ;
}
// This extra param just confuses things, remove it
if ( $ret [ 'network' ] === NETWORK_DIASPORA ) {
$ret [ 'url' ] = str_replace ( '?absolute=true' , '' , $ret [ 'url' ]);
}
// do we have enough information?
if ( ! (( x ( $ret , 'name' )) && ( x ( $ret , 'poll' )) && (( x ( $ret , 'url' )) || ( x ( $ret , 'addr' ))))) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'The profile address specified does not provide adequate information.' ) . EOL ;
2018-01-09 09:40:45 -05:00
if ( ! x ( $ret , 'poll' )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'No compatible communication protocols or feeds were discovered.' ) . EOL ;
2018-01-09 09:40:45 -05:00
}
if ( ! x ( $ret , 'name' )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'An author or name was not found.' ) . EOL ;
2018-01-09 09:40:45 -05:00
}
if ( ! x ( $ret , 'url' )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'No browser URL could be matched to this address.' ) . EOL ;
2018-01-09 09:40:45 -05:00
}
if ( strpos ( $url , '@' ) !== false ) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'Unable to match @-style Identity Address with a known protocol or email contact.' ) . EOL ;
$result [ 'message' ] .= L10n :: t ( 'Use mailto: in front of address to force email check.' ) . EOL ;
2018-01-09 09:40:45 -05:00
}
return $result ;
}
if ( $ret [ 'network' ] === NETWORK_OSTATUS && Config :: get ( 'system' , 'ostatus_disabled' )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'The profile address specified belongs to a network which has been disabled on this site.' ) . EOL ;
2018-01-09 09:40:45 -05:00
$ret [ 'notify' ] = '' ;
}
if ( ! $ret [ 'notify' ]) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'Limited profile. This person will be unable to receive direct/personal notifications from you.' ) . EOL ;
2018-01-09 09:40:45 -05:00
}
$writeable = ((( $ret [ 'network' ] === NETWORK_OSTATUS ) && ( $ret [ 'notify' ])) ? 1 : 0 );
$subhub = (( $ret [ 'network' ] === NETWORK_OSTATUS ) ? true : false );
$hidden = (( $ret [ 'network' ] === NETWORK_MAIL ) ? 1 : 0 );
2018-01-15 08:05:12 -05:00
if ( in_array ( $ret [ 'network' ], [ NETWORK_MAIL , NETWORK_DIASPORA ])) {
2018-01-09 09:40:45 -05:00
$writeable = 1 ;
}
if ( DBM :: is_result ( $r )) {
// update contact
$new_relation = (( $r [ 0 ][ 'rel' ] == CONTACT_IS_FOLLOWER ) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING );
2018-01-15 08:05:12 -05:00
$fields = [ 'rel' => $new_relation , 'subhub' => $subhub , 'readonly' => false ];
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'id' => $r [ 0 ][ 'id' ]]);
2018-01-09 09:40:45 -05:00
} else {
2018-01-15 08:05:12 -05:00
$new_relation = (( in_array ( $ret [ 'network' ], [ NETWORK_MAIL ])) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING );
2018-01-09 09:40:45 -05:00
// create contact record
2018-07-20 08:19:26 -04:00
DBA :: insert ( 'contact' , [
2018-01-11 03:37:11 -05:00
'uid' => $uid ,
2018-01-26 21:38:34 -05:00
'created' => DateTimeFormat :: utcNow (),
2018-01-11 03:37:11 -05:00
'url' => $ret [ 'url' ],
'nurl' => normalise_link ( $ret [ 'url' ]),
'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' ],
'pubkey' => $ret [ 'pubkey' ],
'rel' => $new_relation ,
'priority' => $ret [ 'priority' ],
'writable' => $writeable ,
'hidden' => $hidden ,
'blocked' => 0 ,
'readonly' => 0 ,
'pending' => 0 ,
'subhub' => $subhub
]);
2018-01-09 09:40:45 -05:00
}
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [], [ 'url' => $ret [ 'url' ], 'network' => $ret [ 'network' ], 'uid' => $uid ]);
2018-01-10 20:13:51 -05:00
if ( ! DBM :: is_result ( $contact )) {
2018-01-22 09:54:13 -05:00
$result [ 'message' ] .= L10n :: t ( 'Unable to retrieve contact information.' ) . EOL ;
2018-01-09 09:40:45 -05:00
return $result ;
}
2018-01-10 20:13:51 -05:00
$contact_id = $contact [ 'id' ];
2018-01-09 09:40:45 -05:00
$result [ 'cid' ] = $contact_id ;
Group :: addMember ( User :: getDefaultGroup ( $uid , $contact [ " network " ]), $contact_id );
// Update the avatar
2018-01-09 10:02:53 -05:00
self :: updateAvatar ( $ret [ 'photo' ], $uid , $contact_id );
2018-01-09 09:40:45 -05:00
// pull feed and consume it, which should subscribe to the hub.
Worker :: add ( PRIORITY_HIGH , " OnePoll " , $contact_id , " force " );
$r = q ( " SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
2018-01-11 03:37:11 -05:00
WHERE `user` . `uid` = % d AND `contact` . `self` LIMIT 1 " ,
intval ( $uid )
2018-01-09 09:40:45 -05:00
);
if ( DBM :: is_result ( $r )) {
2018-03-16 02:43:10 -04:00
if ( in_array ( $contact [ 'network' ], [ NETWORK_OSTATUS , NETWORK_DFRN ])) {
2018-01-09 09:40:45 -05:00
// create a follow slap
2018-01-15 08:05:12 -05:00
$item = [];
2018-01-09 09:40:45 -05:00
$item [ 'verb' ] = ACTIVITY_FOLLOW ;
$item [ 'follow' ] = $contact [ " url " ];
$slap = OStatus :: salmon ( $item , $r [ 0 ]);
2018-03-16 02:43:10 -04:00
if ( ! empty ( $contact [ 'notify' ])) {
Salmon :: slapper ( $r [ 0 ], $contact [ 'notify' ], $slap );
}
} elseif ( $contact [ 'network' ] == NETWORK_DIASPORA ) {
2018-01-09 09:40:45 -05:00
$ret = Diaspora :: sendShare ( $a -> user , $contact );
logger ( 'share returns: ' . $ret );
}
}
$result [ 'success' ] = true ;
return $result ;
}
2018-01-27 11:13:41 -05:00
public static function updateSslPolicy ( $contact , $new_policy )
{
$ssl_changed = false ;
if (( intval ( $new_policy ) == SSL_POLICY_SELFSIGN || $new_policy === 'self' ) && strstr ( $contact [ 'url' ], 'https:' )) {
$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' ]);
}
if (( intval ( $new_policy ) == SSL_POLICY_FULL || $new_policy === 'full' ) && strstr ( $contact [ 'url' ], 'http:' )) {
$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 08:19:26 -04:00
DBA :: update ( 'contact' , $fields , [ 'id' => $contact [ 'id' ]]);
2018-01-27 11:13:41 -05:00
}
return $contact ;
}
2018-01-28 06:18:08 -05:00
2018-01-28 12:26:39 -05:00
public static function addRelationship ( $importer , $contact , $datarray , $item , $sharing = false ) {
2018-01-28 06:18:08 -05:00
$url = notags ( trim ( $datarray [ 'author-link' ]));
$name = notags ( trim ( $datarray [ 'author-name' ]));
$photo = notags ( trim ( $datarray [ 'author-avatar' ]));
2018-02-14 00:05:00 -05:00
$nick = '' ;
2018-01-28 06:18:08 -05:00
if ( is_object ( $item )) {
$rawtag = $item -> get_item_tags ( NAMESPACE_ACTIVITY , 'actor' );
if ( $rawtag && $rawtag [ 0 ][ 'child' ][ NAMESPACE_POCO ][ 'preferredUsername' ][ 0 ][ 'data' ]) {
$nick = $rawtag [ 0 ][ 'child' ][ NAMESPACE_POCO ][ 'preferredUsername' ][ 0 ][ 'data' ];
}
} else {
$nick = $item ;
}
if ( is_array ( $contact )) {
2018-03-16 02:58:54 -04:00
if (( $contact [ 'rel' ] == CONTACT_IS_SHARING )
2018-01-28 06:18:08 -05:00
|| ( $sharing && $contact [ 'rel' ] == CONTACT_IS_FOLLOWER )) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'rel' => CONTACT_IS_FRIEND , 'writable' => true ],
2018-01-28 06:18:08 -05:00
[ 'id' => $contact [ 'id' ], 'uid' => $importer [ 'uid' ]]);
}
// send email notification to owner?
} else {
2018-07-20 08:19:26 -04:00
if ( DBA :: exists ( 'contact' , [ 'nurl' => normalise_link ( $url ), 'uid' => $importer [ 'uid' ], 'pending' => true ])) {
2018-04-18 01:02:59 -04:00
logger ( 'ignoring duplicated connection request from pending contact ' . $url );
return ;
}
2018-01-28 06:18:08 -05:00
// create contact record
q ( " INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `name`, `nick`, `photo`, `network`, `rel`,
`blocked` , `readonly` , `pending` , `writable` )
VALUES ( % d , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , % d , 0 , 0 , 1 , 1 ) " ,
intval ( $importer [ 'uid' ]),
2018-02-03 20:44:19 -05:00
dbesc ( DateTimeFormat :: utcNow ()),
2018-01-28 06:18:08 -05:00
dbesc ( $url ),
dbesc ( normalise_link ( $url )),
dbesc ( $name ),
dbesc ( $nick ),
dbesc ( $photo ),
dbesc ( NETWORK_OSTATUS ),
intval ( CONTACT_IS_FOLLOWER )
);
2018-02-14 00:05:00 -05:00
$contact_record = [
2018-07-20 08:19:26 -04:00
'id' => DBA :: lastInsertId (),
2018-07-10 08:27:56 -04:00
'network' => NETWORK_OSTATUS ,
'name' => $name ,
'url' => $url ,
'photo' => $photo
2018-02-14 00:05:00 -05:00
];
2018-07-10 08:27:56 -04:00
2018-02-14 00:05:00 -05:00
Contact :: updateAvatar ( $photo , $importer [ " uid " ], $contact_record [ " id " ], true );
2018-01-28 06:18:08 -05:00
/// @TODO Encapsulate this into a function/method
2018-02-14 00:05:00 -05:00
$fields = [ 'uid' , 'username' , 'email' , 'page-flags' , 'notify-flags' , 'language' ];
2018-07-20 08:19:26 -04:00
$user = DBA :: selectFirst ( 'user' , $fields , [ 'uid' => $importer [ 'uid' ]]);
2018-02-14 00:05:00 -05:00
if ( DBM :: is_result ( $user ) && ! in_array ( $user [ 'page-flags' ], [ PAGE_SOAPBOX , PAGE_FREELOVE , PAGE_COMMUNITY ])) {
2018-01-28 06:18:08 -05:00
// create notification
$hash = random_string ();
if ( is_array ( $contact_record )) {
2018-07-20 08:19:26 -04:00
DBA :: insert ( 'intro' , [ 'uid' => $importer [ 'uid' ], 'contact-id' => $contact_record [ 'id' ],
2018-01-28 06:18:08 -05:00
'blocked' => false , 'knowyou' => false ,
2018-02-03 20:44:19 -05:00
'hash' => $hash , 'datetime' => DateTimeFormat :: utcNow ()]);
2018-01-28 06:18:08 -05:00
}
Group :: addMember ( User :: getDefaultGroup ( $importer [ 'uid' ], $contact_record [ " network " ]), $contact_record [ 'id' ]);
2018-02-14 00:05:00 -05:00
if (( $user [ 'notify-flags' ] & NOTIFY_INTRO ) &&
in_array ( $user [ 'page-flags' ], [ PAGE_NORMAL ])) {
2018-01-28 06:18:08 -05:00
notification ([
'type' => NOTIFY_INTRO ,
2018-02-14 00:05:00 -05:00
'notify_flags' => $user [ 'notify-flags' ],
'language' => $user [ 'language' ],
'to_name' => $user [ 'username' ],
'to_email' => $user [ 'email' ],
'uid' => $user [ 'uid' ],
2018-01-28 06:18:08 -05:00
'link' => System :: baseUrl () . '/notifications/intro' ,
'source_name' => (( strlen ( stripslashes ( $contact_record [ 'name' ]))) ? stripslashes ( $contact_record [ 'name' ]) : L10n :: t ( '[Name Withheld]' )),
'source_link' => $contact_record [ 'url' ],
'source_photo' => $contact_record [ 'photo' ],
'verb' => ( $sharing ? ACTIVITY_FRIEND : ACTIVITY_FOLLOW ),
'otype' => 'intro'
]);
}
2018-02-14 00:05:00 -05:00
} elseif ( DBM :: is_result ( $user ) && in_array ( $user [ 'page-flags' ], [ PAGE_SOAPBOX , PAGE_FREELOVE , PAGE_COMMUNITY ])) {
2018-01-28 06:18:08 -05:00
q ( " UPDATE `contact` SET `pending` = 0 WHERE `uid` = %d AND `url` = '%s' AND `pending` LIMIT 1 " ,
intval ( $importer [ 'uid' ]),
dbesc ( $url )
);
}
}
}
2018-01-28 12:26:39 -05:00
public static function removeFollower ( $importer , $contact , array $datarray = [], $item = " " ) {
2018-01-28 06:18:08 -05:00
if (( $contact [ 'rel' ] == CONTACT_IS_FRIEND ) || ( $contact [ 'rel' ] == CONTACT_IS_SHARING )) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'rel' => CONTACT_IS_SHARING ], [ 'id' => $contact [ 'id' ]]);
2018-01-28 06:18:08 -05:00
} else {
Contact :: remove ( $contact [ 'id' ]);
}
}
2018-01-28 12:26:39 -05:00
public static function removeSharer ( $importer , $contact , array $datarray = [], $item = " " ) {
2018-01-28 06:18:08 -05:00
if (( $contact [ 'rel' ] == CONTACT_IS_FRIEND ) || ( $contact [ 'rel' ] == CONTACT_IS_FOLLOWER )) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'contact' , [ 'rel' => CONTACT_IS_FOLLOWER ], [ 'id' => $contact [ 'id' ]]);
2018-01-28 06:18:08 -05:00
} else {
Contact :: remove ( $contact [ 'id' ]);
}
}
2018-01-24 20:47:33 -05:00
/**
* @ brief Create a birthday event .
*
* Update the year and the birthday .
*/
public static function updateBirthdays ()
{
// This only handles foreign or alien networks where a birthday has been provided.
// In-network birthdays are handled within local_delivery
$r = q ( " SELECT * FROM `contact` WHERE `bd` != '' AND `bd` > '0001-01-01' AND SUBSTRING(`bd`, 1, 4) != `bdyear` " );
if ( DBM :: is_result ( $r )) {
foreach ( $r as $rr ) {
logger ( 'update_contact_birthday: ' . $rr [ 'bd' ]);
2018-01-26 21:38:34 -05:00
$nextbd = DateTimeFormat :: utcNow ( 'Y' ) . substr ( $rr [ 'bd' ], 4 );
2018-01-24 20:47:33 -05:00
/*
* Add new birthday event for this person
*
* $bdtext is just a readable placeholder in case the event is shared
* with others . We will replace it during presentation to our $importer
* to contain a sparkle link and perhaps a photo .
*/
// Check for duplicates
$s = q ( " SELECT `id` FROM `event` WHERE `uid` = %d AND `cid` = %d AND `start` = '%s' AND `type` = '%s' LIMIT 1 " ,
2018-01-26 21:38:34 -05:00
intval ( $rr [ 'uid' ]), intval ( $rr [ 'id' ]), dbesc ( DateTimeFormat :: utc ( $nextbd )), dbesc ( 'birthday' ));
2018-01-24 20:47:33 -05:00
if ( DBM :: is_result ( $s )) {
continue ;
}
$bdtext = L10n :: t ( '%s\'s birthday' , $rr [ 'name' ]);
$bdtext2 = L10n :: t ( 'Happy Birthday %s' , ' [url=' . $rr [ 'url' ] . ']' . $rr [ 'name' ] . '[/url]' );
q ( " INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`,`adjust`)
VALUES ( % d , % d , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%d' ) " , intval( $rr['uid'] ), intval( $rr['id'] ),
2018-01-26 21:38:34 -05:00
dbesc ( DateTimeFormat :: utcNow ()), dbesc ( DateTimeFormat :: utcNow ()), dbesc ( DateTimeFormat :: utc ( $nextbd )),
dbesc ( DateTimeFormat :: utc ( $nextbd . ' + 1 day ' )), dbesc ( $bdtext ), dbesc ( $bdtext2 ), dbesc ( 'birthday' ),
2018-01-24 20:47:33 -05:00
intval ( 0 )
);
// update bdyear
q ( " UPDATE `contact` SET `bdyear` = '%s', `bd` = '%s' WHERE `uid` = %d AND `id` = %d " , dbesc ( substr ( $nextbd , 0 , 4 )),
dbesc ( $nextbd ), intval ( $rr [ 'uid' ]), intval ( $rr [ 'id' ])
);
}
}
}
2018-02-25 19:45:04 -05:00
/**
* Remove the unavailable contact ids from the provided list
*
* @ param array $contact_ids Contact id list
*/
public static function pruneUnavailable ( array & $contact_ids )
{
if ( empty ( $contact_ids )) {
return ;
}
$str = dbesc ( implode ( ',' , $contact_ids ));
2018-07-20 08:19:26 -04:00
$stmt = DBA :: p ( " SELECT `id` FROM `contact` WHERE `id` IN ( " . $str . " ) AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0 " );
2018-02-25 19:45:04 -05:00
$return = [];
2018-07-20 08:19:26 -04:00
while ( $contact = DBA :: fetch ( $stmt )) {
2018-02-25 19:45:04 -05:00
$return [] = $contact [ 'id' ];
}
2018-07-20 08:19:26 -04:00
DBA :: close ( $stmt );
2018-03-03 07:41:49 -05:00
2018-02-25 19:45:04 -05:00
$contact_ids = $return ;
}
2018-06-01 02:46:34 -04:00
/**
* @ brief Returns a magic link to authenticate remote visitors
*
* @ param string $contact_url The address of the target contact profile
* @ param integer $url An url that we will be redirected to after the authentication
*
* @ return string with " redir " link
*/
public static function magicLink ( $contact_url , $url = '' )
{
2018-06-02 04:05:06 -04:00
$cid = self :: getIdForURL ( $contact_url , 0 , true );
2018-06-01 02:46:34 -04:00
if ( empty ( $cid )) {
2018-06-02 09:00:47 -04:00
return $url ? : $contact_url ; // Equivalent to: ($url != '') ? $url : $contact_url;
2018-06-01 02:46:34 -04:00
}
return self :: magicLinkbyId ( $cid , $url );
}
/**
* @ brief Returns a magic link to authenticate remote visitors
*
* @ param integer $cid The contact id of the target contact profile
* @ param integer $url An url that we will be redirected to after the authentication
*
* @ return string with " redir " link
*/
public static function magicLinkbyId ( $cid , $url = '' )
{
2018-07-20 08:19:26 -04:00
$contact = DBA :: selectFirst ( 'contact' , [ 'id' , 'network' , 'url' , 'uid' ], [ 'id' => $cid ]);
2018-06-01 02:46:34 -04:00
2018-07-02 01:41:55 -04:00
return self :: magicLinkbyContact ( $contact , $url );
}
/**
* @ brief Returns a magic link to authenticate remote visitors
*
* @ param array $contact The contact array with " uid " , " network " and " url "
* @ param integer $url An url that we will be redirected to after the authentication
*
* @ return string with " redir " link
*/
public static function magicLinkbyContact ( $contact , $url = '' )
{
2018-06-01 02:46:34 -04:00
if ( $contact [ 'network' ] != NETWORK_DFRN ) {
2018-06-02 09:00:47 -04:00
return $url ? : $contact [ 'url' ]; // Equivalent to ($url != '') ? $url : $contact['url'];
2018-06-01 02:46:34 -04:00
}
2018-06-02 04:42:46 -04: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 ;
}
2018-06-02 04:05:06 -04:00
if ( $contact [ 'uid' ] != 0 ) {
return self :: magicLink ( $contact [ 'url' ], $url );
}
2018-07-02 01:41:55 -04:00
$redirect = 'redir/' . $contact [ 'id' ];
2018-06-01 02:46:34 -04:00
if ( $url != '' ) {
$redirect .= '?url=' . $url ;
}
return $redirect ;
}
2017-11-19 16:55:28 -05:00
}