2017-11-19 21:55:28 +00:00
< ? php
/**
2017-12-08 04:21:51 +00:00
* @ file src / Object / Contact . php
2017-11-19 21:55:28 +00:00
*/
2017-12-08 04:21:51 +00:00
namespace Friendica\Object ;
2017-11-19 21:55:28 +00:00
2017-12-08 04:21:51 +00:00
use Friendica\App ;
2017-11-19 21:55:28 +00:00
use Friendica\BaseObject ;
use Friendica\Core\PConfig ;
use Friendica\Core\System ;
use Friendica\Core\Worker ;
use Friendica\Database\DBM ;
use Friendica\Network\Probe ;
2017-12-08 04:21:51 +00:00
use Friendica\Object\Photo ;
2017-11-19 21:55:28 +00:00
use Friendica\Protocol\Diaspora ;
use Friendica\Protocol\DFRN ;
use Friendica\Protocol\OStatus ;
2017-12-02 14:32:45 +00:00
use Friendica\Protocol\Salmon ;
2017-11-19 21:55:28 +00:00
use dba ;
require_once 'boot.php' ;
require_once 'include/text.php' ;
2017-11-22 14:02:55 +00:00
/**
* @ brief functions for interacting with a contact
*/
2017-11-19 21:55:28 +00:00
class Contact extends BaseObject
{
2017-12-04 03:27:49 +00: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
if ( dba :: exists ( 'contact' , [ 'uid' => intval ( $uid ), 'self' ])) {
return true ;
}
$user = dba :: select ( 'user' , [ 'uid' , 'username' , 'nickname' ], [ 'uid' => intval ( $uid )], [ 'limit' => 1 ]);
if ( ! DBM :: is_result ( $user )) {
return false ;
}
$return = dba :: insert ( 'contact' , [
'uid' => $user [ 'uid' ],
'created' => datetime_convert (),
'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' ],
'name-date' => datetime_convert (),
'uri-date' => datetime_convert (),
'avatar-date' => datetime_convert (),
'closeness' => 0
]);
return $return ;
}
2017-11-19 21:55:28 +00:00
/**
* @ brief Marks a contact for removal
*
2017-11-22 14:02:55 +00:00
* @ param int $id contact id
2017-11-19 21:55:28 +00:00
* @ return null
*/
public static function remove ( $id )
{
// We want just to make sure that we don't delete our "self" contact
2017-11-23 13:15:38 +00:00
$r = dba :: select ( 'contact' , array ( 'uid' ), array ( 'id' => $id , 'self' => false ), array ( 'limit' => 1 ));
2017-11-22 21:29:47 +00:00
2017-11-23 04:13:12 +00:00
if ( ! DBM :: is_result ( $r ) || ! intval ( $r [ 'uid' ])) {
2017-11-19 21:55:28 +00:00
return ;
}
2017-11-23 04:13:12 +00:00
$archive = PConfig :: get ( $r [ 'uid' ], 'system' , 'archive_removed_contacts' );
2017-11-19 21:55:28 +00:00
if ( $archive ) {
2017-11-23 13:15:38 +00:00
dba :: update ( 'contact' , array ( 'archive' => true , 'network' => 'none' , 'writable' => false ), array ( 'id' => $id ));
2017-11-19 21:55:28 +00:00
return ;
}
dba :: delete ( 'contact' , array ( 'id' => $id ));
// 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 14:02:55 +00:00
* @ param array $user User unfriending
2017-11-19 21:55:28 +00:00
* @ param array $contact Contact unfriended
2017-11-23 04:13:12 +00:00
* @ return void
2017-11-19 21:55:28 +00:00
*/
public static function terminateFriendship ( array $user , array $contact )
{
if ( $contact [ 'network' ] === NETWORK_OSTATUS ) {
// create an unfollow slap
$item = array ();
$item [ 'verb' ] = NAMESPACE_OSTATUS . " /unfollow " ;
$item [ 'follow' ] = $contact [ " url " ];
$slap = OStatus :: salmon ( $item , $user );
if (( x ( $contact , 'notify' )) && ( strlen ( $contact [ 'notify' ]))) {
2017-12-02 14:32:45 +00:00
Salmon :: slapper ( $user , $contact [ 'notify' ], $slap );
2017-11-19 21:55:28 +00:00
}
} elseif ( $contact [ 'network' ] === NETWORK_DIASPORA ) {
Diaspora :: sendUnshare ( $user , $contact );
} elseif ( $contact [ 'network' ] === NETWORK_DFRN ) {
DFRN :: deliver ( $user , $contact , 'placeholder' , 1 );
}
}
/**
* @ 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 14:02:55 +00:00
* @ param array $contact contact to mark for archival
2017-11-19 21:55:28 +00:00
* @ return type
*/
public static function markForArchival ( array $contact )
{
2017-12-05 21:29:33 +00:00
// Contact already archived or "self" contact? => nothing to do
if ( $contact [ 'archive' ] || $contact [ 'self' ]) {
2017-11-19 21:55:28 +00:00
return ;
}
2017-12-04 19:37:36 +00:00
2017-11-19 21:55:28 +00:00
if ( $contact [ 'term-date' ] <= NULL_DATE ) {
2017-11-22 21:29:47 +00:00
dba :: update ( 'contact' , array ( 'term-date' => datetime_convert ()), array ( 'id' => $contact [ 'id' ]));
2017-11-19 21:55:28 +00:00
if ( $contact [ 'url' ] != '' ) {
2017-12-05 21:29:33 +00:00
dba :: update ( 'contact' , array ( 'term-date' => datetime_convert ()), array ( '`nurl` = ? AND `term-date` <= ? AND NOT `self`' , normalise_link ( $contact [ 'url' ]), NULL_DATE ));
2017-11-19 21:55:28 +00: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
$expiry = $contact [ 'term-date' ] . ' + 32 days ' ;
if ( datetime_convert () > datetime_convert ( 'UTC' , 'UTC' , $expiry )) {
/* 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 .
*/
2017-11-22 21:29:47 +00:00
dba :: update ( 'contact' , array ( 'archive' => 1 ), array ( 'id' => $contact [ 'id' ]));
2017-11-19 21:55:28 +00:00
if ( $contact [ 'url' ] != '' ) {
2017-12-05 21:29:33 +00:00
dba :: update ( 'contact' , array ( 'archive' => 1 ), array ( 'nurl' => normalise_link ( $contact [ 'url' ]), 'self' => false ));
2017-11-19 21:55:28 +00:00
}
}
}
}
/**
* @ brief Cancels the archival countdown
*
* @ see Contact :: markForArchival ()
*
2017-11-22 14:02:55 +00:00
* @ param array $contact contact to be unmarked for archival
2017-11-19 21:55:28 +00:00
* @ return null
*/
public static function unmarkForArchival ( array $contact )
{
2017-12-04 19:36:08 +00:00
$condition = array ( '`id` = ? AND (`term-date` > ? OR `archive`)' , $contact [ 'id' ], NULL_DATE );
2017-11-23 13:15:38 +00:00
$exists = dba :: exists ( 'contact' , $condition );
2017-11-19 21:55:28 +00:00
// We don't need to update, we never marked this contact for archival
2017-11-23 13:15:38 +00:00
if ( ! $exists ) {
2017-11-19 21:55:28 +00:00
return ;
}
// It's a miracle. Our dead contact has inexplicably come back to life.
$fields = array ( 'term-date' => NULL_DATE , 'archive' => false );
dba :: update ( 'contact' , $fields , array ( 'id' => $contact [ 'id' ]));
if ( $contact [ 'url' ] != '' ) {
dba :: update ( 'contact' , $fields , array ( 'nurl' => normalise_link ( $contact [ 'url' ])));
}
}
/**
* @ 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 14:02:55 +00: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 21:55:28 +00:00
*
* @ return array Contact data
*/
public static function getDetailsByURL ( $url , $uid = - 1 , array $default = [])
{
static $cache = array ();
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
$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`,
`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 );
$r = dba :: inArray ( $s );
// Fetch contact data from the contact table for the given user, checking with the alias
if ( ! DBM :: is_result ( $r )) {
$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`,
`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 );
$r = dba :: inArray ( $s );
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
if ( ! DBM :: is_result ( $r )) {
$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`,
`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 ));
$r = dba :: inArray ( $s );
}
// Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias
if ( ! DBM :: is_result ( $r )) {
$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`,
`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 );
$r = dba :: inArray ( $s );
}
// Fetch the data from the gcontact table
if ( ! DBM :: is_result ( $r )) {
$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`,
`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 `nurl` = ? " , normalise_link( $url ));
$r = dba :: inArray ( $s );
}
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 14:02:55 +00:00
foreach ( $r as $id => $result ) {
2017-11-19 21:55:28 +00: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 ;
}
if (( $profile [ " photo " ] == " " ) && isset ( $default [ " photo " ])) {
$profile [ " photo " ] = $default [ " photo " ];
}
if (( $profile [ " name " ] == " " ) && isset ( $default [ " name " ])) {
$profile [ " name " ] = $default [ " name " ];
}
if (( $profile [ " network " ] == " " ) && isset ( $default [ " network " ])) {
$profile [ " network " ] = $default [ " network " ];
}
if (( $profile [ " thumb " ] == " " ) && isset ( $profile [ " photo " ])) {
$profile [ " thumb " ] = $profile [ " photo " ];
}
if (( $profile [ " micro " ] == " " ) && isset ( $profile [ " thumb " ])) {
$profile [ " micro " ] = $profile [ " thumb " ];
}
2017-11-22 14:02:55 +00:00
if ((( $profile [ " addr " ] == " " ) || ( $profile [ " name " ] == " " )) && ( $profile [ " gid " ] != 0 )
&& in_array ( $profile [ " network " ], array ( NETWORK_DFRN , NETWORK_DIASPORA , NETWORK_OSTATUS ))
) {
2017-11-19 21:55:28 +00:00
Worker :: add ( PRIORITY_LOW , " UpdateGContact " , $profile [ " gid " ]);
}
// Show contact details of Diaspora contacts only if connected
if (( $profile [ " cid " ] == 0 ) && ( $profile [ " network " ] == NETWORK_DIASPORA )) {
$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 14:02:55 +00:00
* @ param int $uid User id
2017-11-19 21:55:28 +00:00
*
* @ return array Contact data
*/
public static function getDetailsByAddr ( $addr , $uid = - 1 )
{
static $cache = array ();
if ( $addr == '' ) {
return array ();
}
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`
FROM `contact` WHERE `addr` = '%s' AND `uid` = % d " , dbesc( $addr ), intval( $uid ));
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
if ( ! DBM :: is_result ( $r ))
$r = q ( " SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `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` , 0 AS `self`
FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 " , dbesc( $addr ));
// Fetch the data from the gcontact table
if ( ! DBM :: is_result ( $r ))
$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`,
`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 ));
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 14:02:55 +00:00
* @ param array $contact contact
* @ param int $uid optional , default 0
2017-11-19 21:55:28 +00: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 ();
}
if ( $contact [ 'uid' ] != $uid ) {
if ( $uid == 0 ) {
$profile_link = zrl ( $contact [ 'url' ]);
2017-11-22 14:02:55 +00:00
$menu = array ( 'profile' => array ( t ( 'View Profile' ), $profile_link , true ));
2017-11-19 21:55:28 +00:00
return $menu ;
}
2017-11-23 04:32:24 +00:00
$r = dba :: select ( 'contact' , array (), array ( 'nurl' => $contact [ 'nurl' ], 'network' => $contact [ 'network' ], 'uid' => $uid ), array ( 'limit' => 1 ));
2017-11-19 21:55:28 +00:00
if ( $r ) {
2017-11-23 04:32:24 +00:00
return self :: photoMenu ( $r , $uid );
2017-11-19 21:55:28 +00:00
} else {
$profile_link = zrl ( $contact [ 'url' ]);
$connlnk = 'follow/?url=' . $contact [ 'url' ];
$menu = array (
'profile' => array ( t ( 'View Profile' ), $profile_link , true ),
'follow' => array ( t ( 'Connect/Follow' ), $connlnk , true )
);
return $menu ;
}
}
$sparkle = false ;
if ( $contact [ 'network' ] === NETWORK_DFRN ) {
$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' ;
}
if ( in_array ( $contact [ 'network' ], array ( NETWORK_DFRN , NETWORK_DIASPORA ))) {
$pm_url = System :: baseUrl () . '/message/new/' . $contact [ 'id' ];
}
if ( $contact [ 'network' ] == NETWORK_DFRN ) {
$poke_link = System :: baseUrl () . '/poke/?f=&c=' . $contact [ 'id' ];
}
$contact_url = System :: baseUrl () . '/contacts/' . $contact [ 'id' ];
$posts_link = System :: baseUrl () . '/contacts/' . $contact [ 'id' ] . '/posts' ;
$contact_drop_link = System :: baseUrl () . '/contacts/' . $contact [ 'id' ] . '/drop?confirm=1' ;
/**
2017-11-22 14:02:55 +00:00
* Menu array :
2017-11-19 21:55:28 +00:00
* " name " => [ " Label " , " link " , ( bool ) Should the link opened in a new tab ? ]
*/
$menu = array (
'status' => array ( t ( " View Status " ), $status_link , true ),
'profile' => array ( t ( " View Profile " ), $profile_link , true ),
'photos' => array ( t ( " View Photos " ), $photos_link , true ),
'network' => array ( t ( " Network Posts " ), $posts_link , false ),
'edit' => array ( t ( " View Contact " ), $contact_url , false ),
'drop' => array ( t ( " Drop Contact " ), $contact_drop_link , false ),
'pm' => array ( t ( " Send PM " ), $pm_url , false ),
'poke' => array ( t ( " Poke " ), $poke_link , false ),
);
$args = array ( 'contact' => $contact , 'menu' => & $menu );
call_hooks ( 'contact_photo_menu' , $args );
$menucondensed = array ();
2017-11-22 14:02:55 +00:00
foreach ( $menu as $menuname => $menuitem ) {
2017-11-19 21:55:28 +00:00
if ( $menuitem [ 1 ] != '' ) {
$menucondensed [ $menuname ] = $menuitem ;
}
}
return $menucondensed ;
}
/**
2017-11-22 14:02:55 +00:00
* @ brief Returns ungrouped contact count or list for user
*
2017-11-19 21:55:28 +00:00
* Returns either the total number of ungrouped contacts for the given user
* id or a paginated list of ungrouped contacts .
*
2017-11-22 14:02:55 +00:00
* @ param int $uid uid
* @ param int $start optional , default 0
* @ param int $count optional , default 0
2017-11-19 21:55:28 +00:00
*
* @ return array
*/
public static function getUngroupedList ( $uid , $start = 0 , $count = 0 )
{
if ( ! $count ) {
2017-11-23 13:15:38 +00:00
$r = q (
" SELECT COUNT(*) AS `total`
FROM `contact`
WHERE `uid` = % d
2017-11-29 20:59:06 +00:00
AND NOT `self`
AND NOT `blocked`
AND NOT `pending`
2017-11-23 13:15:38 +00:00
AND `id` NOT IN (
SELECT DISTINCT ( `contact-id` )
FROM `group_member`
WHERE `uid` = % d
2017-11-29 20:59:06 +00:00
) " , intval( $uid ), intval( $uid )
2017-11-23 13:15:38 +00:00
);
2017-11-24 00:22:30 +00:00
2017-11-23 13:15:38 +00:00
return $r ;
}
$r = q (
" SELECT *
FROM `contact`
WHERE `uid` = % d
2017-11-29 20:59:06 +00:00
AND NOT `self`
AND NOT `blocked`
AND NOT `pending`
2017-11-23 13:15:38 +00:00
AND `id` NOT IN (
SELECT DISTINCT ( `contact-id` )
FROM `group_member` WHERE `uid` = % d
)
LIMIT % d , % d " , intval( $uid ), intval( $uid ), intval( $start ), intval( $count )
);
return $r ;
2017-11-19 21:55:28 +00:00
}
/**
* @ brief Fetch the contact id for a given url and user
*
* 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 .
*
* Second , we probe the provided $url wether it ' s http :// server . tld / profile or
* 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 14:02:55 +00:00
* @ param string $url Contact URL
* @ param integer $uid The user id for the contact ( 0 = public contact )
2017-11-19 21:55:28 +00:00
* @ param boolean $no_update Don ' t update the contact
*
* @ return integer Contact ID
*/
2017-11-19 22:03:39 +00:00
public static function getIdForURL ( $url , $uid = 0 , $no_update = false )
2017-11-19 21:55:28 +00: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
$contact = dba :: select ( 'contact' , array ( 'id' , 'avatar-date' ), array ( 'nurl' => normalise_link ( $url ), 'uid' => $uid ), array ( 'limit' => 1 ));
// Then the addr (nick@server.tld)
if ( ! DBM :: is_result ( $contact )) {
$contact = dba :: select ( 'contact' , array ( 'id' , 'avatar-date' ), array ( 'addr' => $url , 'uid' => $uid ), array ( 'limit' => 1 ));
}
// 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 );
2017-11-22 21:29:47 +00:00
$r = dba :: select ( 'contact' , array ( 'id' , 'avatar-date' ), array ( '`alias` IN (?, ?, ?) AND `uid` = ?' , $url , normalise_link ( $url ), $ssl_url , $uid ), array ( 'limit' => 1 ));
2017-11-19 21:55:28 +00:00
$contact = dba :: fetch ( $r );
dba :: close ( $r );
}
if ( DBM :: is_result ( $contact )) {
$contact_id = $contact [ " id " ];
// Update the contact every 7 days
$update_contact = ( $contact [ 'avatar-date' ] < datetime_convert ( '' , '' , 'now -7 days' ));
// We force the update if the avatar is empty
if ( $contact [ 'avatar' ] == '' ) {
$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
if ( ! in_array ( $data [ " network " ], array ( NETWORK_DFRN , NETWORK_OSTATUS , NETWORK_DIASPORA , NETWORK_PUMPIO , NETWORK_MAIL ))) {
if ( $uid != 0 ) {
return 0 ;
}
// Get data from the gcontact table
$gcontacts = dba :: select ( 'gcontact' , array ( 'name' , 'nick' , 'url' , 'photo' , 'addr' , 'alias' , 'network' ), array ( 'nurl' => normalise_link ( $url )), array ( 'limit' => 1 ));
if ( ! DBM :: is_result ( $gcontacts )) {
return 0 ;
}
$data = array_merge ( $data , $gcontacts );
}
if ( ! $contact_id && ( $data [ " alias " ] != '' ) && ( $data [ " alias " ] != $url )) {
2017-11-19 22:03:39 +00:00
$contact_id = self :: getIdForURL ( $data [ " alias " ], $uid , true );
2017-11-19 21:55:28 +00:00
}
$url = $data [ " url " ];
if ( ! $contact_id ) {
2017-11-22 21:29:47 +00:00
dba :: insert (
'contact' , array ( 'uid' => $uid , 'created' => datetime_convert (), 'url' => $data [ " url " ],
2017-11-19 21:55:28 +00:00
'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 " ],
'name-date' => datetime_convert (), 'uri-date' => datetime_convert (),
'avatar-date' => datetime_convert (), 'writable' => 1 , 'blocked' => 0 ,
2017-11-22 21:29:47 +00:00
'readonly' => 0 , 'pending' => 0 )
);
2017-11-19 21:55:28 +00:00
2017-11-24 07:44:02 +00:00
$s = dba :: select ( 'contact' , array ( 'id' ), array ( 'nurl' => normalise_link ( $data [ " url " ]), 'uid' => $uid ), array ( 'order' => array ( 'id' ), 'limit' => 2 ));
2017-11-22 21:29:47 +00:00
$contacts = dba :: inArray ( $s );
2017-11-19 21:55:28 +00:00
if ( ! DBM :: is_result ( $contacts )) {
return 0 ;
}
$contact_id = $contacts [ 0 ][ " id " ];
// Update the newly created contact from data in the gcontact table
$gcontact = dba :: select ( 'gcontact' , array ( 'location' , 'about' , 'keywords' , 'gender' ), array ( 'nurl' => normalise_link ( $data [ " url " ])), array ( 'limit' => 1 ));
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' ]);
}
dba :: update ( 'contact' , $gcontact , array ( 'id' => $contact_id ));
}
if ( count ( $contacts ) > 1 && $uid == 0 && $contact_id != 0 && $data [ " url " ] != " " ) {
dba :: delete ( 'contact' , array ( " `nurl` = ? AND `uid` = 0 AND `id` != ? AND NOT `self` " ,
normalise_link ( $data [ " url " ]), $contact_id ));
}
}
2017-11-29 22:29:11 +00:00
self :: updateAvatar ( $data [ " photo " ], $uid , $contact_id );
2017-11-19 21:55:28 +00:00
2017-12-07 06:51:38 +00:00
$fields = array ( 'url' , 'nurl' , 'addr' , 'alias' , 'name' , 'nick' , 'keywords' , 'location' , 'about' , 'avatar-date' , 'pubkey' );
$contact = dba :: select ( 'contact' , $fields , array ( 'id' => $contact_id ), array ( 'limit' => 1 ));
2017-11-19 21:55:28 +00:00
// This condition should always be true
if ( ! DBM :: is_result ( $contact )) {
return $contact_id ;
}
$updated = array ( 'addr' => $data [ 'addr' ],
'alias' => $data [ 'alias' ],
'url' => $data [ 'url' ],
'nurl' => normalise_link ( $data [ 'url' ]),
'name' => $data [ 'name' ],
'nick' => $data [ 'nick' ]);
2017-12-07 06:51:38 +00:00
// Only fill the pubkey if it was empty before. We have to prevent identity theft.
if ( ! empty ( $contact [ 'pubkey' ])) {
unset ( $contact [ 'pubkey' ]);
} else {
$updated [ 'pubkey' ] = $data [ 'pubkey' ];
}
2017-11-19 21:55:28 +00:00
if ( $data [ 'keywords' ] != '' ) {
$updated [ 'keywords' ] = $data [ 'keywords' ];
}
if ( $data [ 'location' ] != '' ) {
$updated [ 'location' ] = $data [ 'location' ];
}
if ( $data [ 'about' ] != '' ) {
$updated [ 'about' ] = $data [ 'about' ];
}
if (( $data [ " addr " ] != $contact [ " addr " ]) || ( $data [ " alias " ] != $contact [ " alias " ])) {
$updated [ 'uri-date' ] = datetime_convert ();
}
if (( $data [ " name " ] != $contact [ " name " ]) || ( $data [ " nick " ] != $contact [ " nick " ])) {
$updated [ 'name-date' ] = datetime_convert ();
}
$updated [ 'avatar-date' ] = datetime_convert ();
dba :: update ( 'contact' , $updated , array ( 'id' => $contact_id ), $contact );
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 ;
}
$blocked = dba :: select ( 'contact' , array ( 'blocked' ), array ( 'id' => $cid ), array ( 'limit' => 1 ));
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 ;
}
$hidden = dba :: select ( 'contact' , array ( 'hidden' ), array ( 'id' => $cid ), array ( 'limit' => 1 ));
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 22:15:35 +00:00
$a = self :: getApp ();
2017-11-19 21:55:28 +00: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 13:15:38 +00:00
$r = q ( " SELECT `network`, `id` AS `author-id`, `contact-type` FROM `contact`
WHERE `contact` . `nurl` = '%s' AND `contact` . `uid` = 0 " , dbesc(normalise_link( $contact_url )));
2017-11-19 21:55:28 +00:00
if ( ! DBM :: is_result ( $r )) {
return '' ;
}
2017-11-23 13:15:38 +00:00
if ( in_array ( $r [ 0 ][ " network " ], array ( NETWORK_DFRN , NETWORK_DIASPORA , NETWORK_OSTATUS , " " ))) {
2017-11-19 21:55:28 +00:00
$sql = " (`item`.`uid` = 0 OR (`item`.`uid` = %d AND NOT `item`.`global`)) " ;
} else {
$sql = " `item`.`uid` = %d " ;
}
2017-11-23 13:15:38 +00:00
$author_id = intval ( $r [ 0 ][ " author-id " ]);
2017-11-19 21:55:28 +00:00
2017-11-23 13:15:38 +00:00
$contact = ( $r [ 0 ][ " contact-type " ] == ACCOUNT_TYPE_COMMUNITY ? 'owner-id' : 'author-id' );
2017-11-19 21:55:28 +00:00
$r = q ( item_query () . " AND `item`.` " . $contact . " ` = %d AND " . $sql .
" ORDER BY `item`.`created` DESC LIMIT %d, %d " , intval ( $author_id ), intval ( local_user ()), intval ( $a -> pager [ 'start' ]), intval ( $a -> pager [ 'itemspage' ])
);
$o = conversation ( $a , $r , 'community' , false );
$o .= alt_pager ( $a , count ( $r ));
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 21:29:47 +00:00
if (( isset ( $contact [ 'page-flags' ]) && ( intval ( $contact [ 'page-flags' ]) == PAGE_COMMUNITY ))
2017-11-19 21:55:28 +00: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 " ];
}
if ( isset ( $contact [ " account-type " ])) {
$type = $contact [ " account-type " ];
}
switch ( $type ) {
case ACCOUNT_TYPE_ORGANISATION :
$account_type = t ( " Organisation " );
break ;
case ACCOUNT_TYPE_NEWS :
$account_type = t ( 'News' );
break ;
case ACCOUNT_TYPE_COMMUNITY :
$account_type = t ( " Forum " );
break ;
default :
$account_type = " " ;
break ;
}
return $account_type ;
}
2017-11-29 22:29:11 +00:00
/**
2017-12-01 05:41:26 +00:00
* @ brief Blocks a contact
*
* @ param int $uid
* @ return bool
*/
public static function block ( $uid )
{
$return = dba :: update ( 'contact' , [ 'blocked' => true ], [ 'id' => $uid ]);
return $return ;
}
/**
* @ brief Unblocks a contact
*
* @ param int $uid
* @ return bool
*/
public static function unblock ( $uid )
{
$return = dba :: update ( 'contact' , [ 'blocked' => false ], [ 'id' => $uid ]);
return $return ;
2017-12-01 05:48:13 +00:00
}
/**
* @ brief Updates the avatar links in a contact only if needed
2017-11-29 22:29:11 +00: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 )
{
// Limit = 1 returns the row so no need for dba:inArray()
$r = dba :: select ( 'contact' , array ( 'avatar' , 'photo' , 'thumb' , 'micro' , 'nurl' ), array ( 'id' => $cid ), array ( 'limit' => 1 ));
if ( ! DBM :: is_result ( $r )) {
return false ;
} else {
$data = array ( $r [ " photo " ], $r [ " thumb " ], $r [ " micro " ]);
}
if (( $r [ " avatar " ] != $avatar ) || $force ) {
2017-12-08 04:21:51 +00:00
$photos = Photo :: importProfilePhoto ( $avatar , $uid , $cid , true );
2017-11-29 22:29:11 +00:00
if ( $photos ) {
dba :: update (
'contact' ,
array ( 'avatar' => $avatar , 'photo' => $photos [ 0 ], 'thumb' => $photos [ 1 ], 'micro' => $photos [ 2 ], 'avatar-date' => datetime_convert ()),
array ( 'id' => $cid )
);
// Update the public contact (contact id = 0)
if ( $uid != 0 ) {
$pcontact = dba :: select ( 'contact' , array ( 'id' ), array ( 'nurl' => $r [ 0 ][ 'nurl' ]), array ( 'limit' => 1 ));
if ( DBM :: is_result ( $pcontact )) {
self :: updateAvatar ( $avatar , 0 , $pcontact [ 'id' ], $force );
}
}
return $photos ;
}
}
return $data ;
}
2017-11-19 21:55:28 +00:00
}