2008-05-07 18:15:42 +01:00
< ? php
2008-05-20 20:14:12 +01:00
/*
2009-08-25 23:14:12 +01:00
* StatusNet - the distributed open - source microblogging tool
2011-07-15 20:13:57 +01:00
* Copyright ( C ) 2008 - 2011 , StatusNet , Inc .
2008-05-20 20:14:12 +01:00
*
2008-05-14 20:26:48 +01:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2008-05-20 20:14:12 +01:00
*
2008-05-14 20:26:48 +01:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
2008-05-20 20:14:12 +01:00
*
2008-05-14 20:26:48 +01:00
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http :// www . gnu . org / licenses />.
*/
2015-12-27 10:50:11 +00:00
if ( ! defined ( 'GNUSOCIAL' )) { exit ( 1 ); }
2008-05-14 20:26:48 +01:00
2008-05-07 18:15:42 +01:00
/**
* Table Definition for profile
*/
2011-08-22 22:52:02 +01:00
class Profile extends Managed_DataObject
2008-05-07 18:15:42 +01:00
{
public $__table = 'profile' ; // table name
public $id ; // int(4) primary_key not_null
2008-07-10 05:51:26 +01:00
public $nickname ; // varchar(64) multiple_key not_null
2015-12-27 10:50:11 +00:00
public $fullname ; // text()
public $profileurl ; // text()
public $homepage ; // text()
2009-08-20 22:09:04 +01:00
public $bio ; // text() multiple_key
2015-12-27 10:50:11 +00:00
public $location ; // text()
2009-09-15 23:28:44 +01:00
public $lat ; // decimal(10,7)
public $lon ; // decimal(10,7)
public $location_id ; // int(4)
public $location_ns ; // int(4)
2008-05-07 18:15:42 +01:00
public $created ; // datetime() not_null
public $modified ; // timestamp() not_null default_CURRENT_TIMESTAMP
2011-08-22 22:52:02 +01:00
public static function schemaDef ()
{
2011-09-18 23:28:44 +01:00
$def = array (
2011-08-22 22:52:02 +01:00
'description' => 'local and remote users have profiles' ,
'fields' => array (
'id' => array ( 'type' => 'serial' , 'not null' => true , 'description' => 'unique identifier' ),
2015-06-03 21:43:51 +01:00
'nickname' => array ( 'type' => 'varchar' , 'length' => 64 , 'not null' => true , 'description' => 'nickname or username' , 'collate' => 'utf8mb4_general_ci' ),
2015-12-27 10:50:11 +00:00
'fullname' => array ( 'type' => 'text' , 'description' => 'display name' , 'collate' => 'utf8mb4_general_ci' ),
'profileurl' => array ( 'type' => 'text' , 'description' => 'URL, cached so we dont regenerate' ),
'homepage' => array ( 'type' => 'text' , 'description' => 'identifying URL' , 'collate' => 'utf8mb4_general_ci' ),
2015-06-03 21:43:51 +01:00
'bio' => array ( 'type' => 'text' , 'description' => 'descriptive biography' , 'collate' => 'utf8mb4_general_ci' ),
2015-12-27 10:50:11 +00:00
'location' => array ( 'type' => 'text' , 'description' => 'physical location' , 'collate' => 'utf8mb4_general_ci' ),
2011-08-22 22:52:02 +01:00
'lat' => array ( 'type' => 'numeric' , 'precision' => 10 , 'scale' => 7 , 'description' => 'latitude' ),
'lon' => array ( 'type' => 'numeric' , 'precision' => 10 , 'scale' => 7 , 'description' => 'longitude' ),
'location_id' => array ( 'type' => 'int' , 'description' => 'location id if possible' ),
'location_ns' => array ( 'type' => 'int' , 'description' => 'namespace for location' ),
'created' => array ( 'type' => 'datetime' , 'not null' => true , 'description' => 'date this record was created' ),
'modified' => array ( 'type' => 'timestamp' , 'not null' => true , 'description' => 'date this record was modified' ),
),
'primary key' => array ( 'id' ),
'indexes' => array (
'profile_nickname_idx' => array ( 'nickname' ),
2011-09-18 22:17:41 +01:00
)
2011-08-22 22:52:02 +01:00
);
2011-09-18 23:28:44 +01:00
// Add a fulltext index
if ( common_config ( 'search' , 'type' ) == 'fulltext' ) {
$def [ 'fulltext indexes' ] = array ( 'nickname' => array ( 'nickname' , 'fullname' , 'location' , 'bio' , 'homepage' ));
}
return $def ;
2011-08-22 22:52:02 +01:00
}
2008-05-20 20:14:12 +01:00
2013-09-19 16:20:44 +01:00
public static function getByEmail ( $email )
{
// in the future, profiles should have emails stored...
$user = User :: getKV ( 'email' , $email );
if ( ! ( $user instanceof User )) {
throw new NoSuchUserException ( array ( 'email' => $email ));
}
return $user -> getProfile ();
}
2014-06-05 23:07:32 +01:00
protected $_user = array ();
2011-04-07 21:55:32 +01:00
2013-09-09 20:35:16 +01:00
public function getUser ()
2009-10-15 10:16:37 +01:00
{
2014-06-05 23:07:32 +01:00
if ( ! isset ( $this -> _user [ $this -> id ])) {
2014-06-05 23:32:07 +01:00
$user = User :: getKV ( 'id' , $this -> id );
if ( ! $user instanceof User ) {
throw new NoSuchUserException ( array ( 'id' => $this -> id ));
}
$this -> _user [ $this -> id ] = $user ;
2011-04-07 21:55:32 +01:00
}
2014-06-05 23:07:32 +01:00
return $this -> _user [ $this -> id ];
}
protected $_group = array ();
2013-10-30 11:56:17 +00:00
public function getGroup ()
{
2014-06-05 23:07:32 +01:00
if ( ! isset ( $this -> _group [ $this -> id ])) {
2014-06-05 23:32:07 +01:00
$group = User_group :: getKV ( 'profile_id' , $this -> id );
if ( ! $group instanceof User_group ) {
throw new NoSuchGroupException ( array ( 'profile_id' => $this -> id ));
}
$this -> _group [ $this -> id ] = $group ;
2013-10-30 11:56:17 +00:00
}
2014-06-05 23:07:32 +01:00
return $this -> _group [ $this -> id ];
}
2013-10-30 11:56:17 +00:00
public function isGroup ()
{
try {
$this -> getGroup ();
return true ;
} catch ( NoSuchGroupException $e ) {
return false ;
}
}
2016-01-13 17:34:48 +00:00
public function isPerson ()
{
// Maybe other things than PERSON and GROUP can have Profiles in the future?
return ! $this -> isGroup ();
}
2013-09-09 20:35:16 +01:00
public function isLocal ()
{
try {
$this -> getUser ();
} catch ( NoSuchUserException $e ) {
return false ;
}
return true ;
}
2015-07-17 00:47:43 +01:00
// Returns false if the user has no password (which will always
// be the case for remote users). This can be the case for OpenID
// logins or other mechanisms which don't store a password hash.
public function hasPassword ()
{
try {
2015-10-04 21:31:07 +01:00
return $this -> getUser () -> hasPassword ();
2015-07-17 00:47:43 +01:00
} catch ( NoSuchUserException $e ) {
return false ;
}
}
2014-07-02 17:39:53 +01:00
public function getObjectType ()
{
// FIXME: More types... like peopletags and whatever
if ( $this -> isGroup ()) {
return ActivityObject :: GROUP ;
} else {
return ActivityObject :: PERSON ;
}
}
2013-09-30 21:13:37 +01:00
public function getAvatar ( $width , $height = null )
2008-12-23 19:33:23 +00:00
{
2013-10-06 14:54:06 +01:00
return Avatar :: byProfile ( $this , $width , $height );
2011-09-30 01:56:24 +01:00
}
2013-10-06 00:56:27 +01:00
public function setOriginal ( $filename )
2008-12-23 19:33:23 +00:00
{
2015-01-27 12:37:50 +00:00
if ( $this -> isGroup ()) {
// Until Group avatars are handled just like profile avatars.
return $this -> getGroup () -> setOriginal ( $filename );
}
2015-03-10 22:52:50 +00:00
$imagefile = new ImageFile ( null , Avatar :: path ( $filename ));
2008-12-23 19:19:07 +00:00
$avatar = new Avatar ();
$avatar -> profile_id = $this -> id ;
2009-02-05 00:32:15 +00:00
$avatar -> width = $imagefile -> width ;
$avatar -> height = $imagefile -> height ;
$avatar -> mediatype = image_type_to_mime_type ( $imagefile -> type );
2008-12-23 19:19:07 +00:00
$avatar -> filename = $filename ;
$avatar -> original = true ;
2013-10-08 10:40:23 +01:00
$avatar -> created = common_sql_now ();
2008-12-23 19:19:07 +00:00
2011-03-22 15:54:23 +00:00
// XXX: start a transaction here
2013-10-01 10:37:59 +01:00
if ( ! Avatar :: deleteFromProfile ( $this , true ) || ! $avatar -> insert ()) {
// If we can't delete the old avatars, let's abort right here.
2009-02-06 08:13:08 +00:00
@ unlink ( Avatar :: path ( $filename ));
2008-12-23 19:21:29 +00:00
return null ;
2008-12-23 19:19:07 +00:00
}
return $avatar ;
}
2010-11-02 20:42:33 +00:00
/**
* Gets either the full name ( if filled ) or the nickname .
*
* @ return string
*/
2008-12-23 19:33:23 +00:00
function getBestName ()
{
2008-12-23 19:19:07 +00:00
return ( $this -> fullname ) ? $this -> fullname : $this -> nickname ;
}
2008-07-07 06:43:58 +01:00
2013-10-30 12:05:04 +00:00
/**
* Takes the currently scoped profile into account to give a name
* to list in notice streams . Preferences may differ between profiles .
*/
function getStreamName ()
{
$user = common_current_user ();
if ( $user instanceof User && $user -> streamNicknames ()) {
return $this -> nickname ;
}
return $this -> getBestName ();
}
2010-11-02 20:42:33 +00:00
/**
* Gets the full name ( if filled ) with nickname as a parenthetical , or the nickname alone
* if no fullname is provided .
*
* @ return string
*/
function getFancyName ()
{
if ( $this -> fullname ) {
2011-03-29 22:00:29 +01:00
// TRANS: Full name of a profile or group (%1$s) followed by nickname (%2$s) in parentheses.
2010-11-02 22:08:59 +00:00
return sprintf ( _m ( 'FANCYNAME' , '%1$s (%2$s)' ), $this -> fullname , $this -> nickname );
2010-11-02 20:42:33 +00:00
} else {
return $this -> nickname ;
}
}
2010-03-11 19:01:01 +00:00
/**
* Get the most recent notice posted by this user , if any .
*
* @ return mixed Notice or null
*/
2016-01-04 20:42:24 +00:00
function getCurrentNotice ( Profile $scoped = null )
2008-12-23 19:33:23 +00:00
{
2016-01-04 20:38:41 +00:00
try {
2016-01-04 20:42:24 +00:00
$notice = $this -> getNotices ( 0 , 1 , 0 , 0 , $scoped );
2016-01-04 20:38:41 +00:00
if ( $notice -> fetch ()) {
if ( $notice instanceof ArrayWrapper ) {
// hack for things trying to work with single notices
// ...but this shouldn't happen anymore I think. Keeping it for safety...
return $notice -> _items [ 0 ];
}
return $notice ;
2011-03-02 01:01:35 +00:00
}
2016-01-04 20:38:41 +00:00
} catch ( PrivateStreamException $e ) {
// Maybe we should let this through if it's handled well upstream
return null ;
2008-12-23 19:19:07 +00:00
}
2014-06-04 22:20:20 +01:00
return null ;
2008-12-23 19:19:07 +00:00
}
2015-07-09 23:28:36 +01:00
function getReplies ( $offset = 0 , $limit = NOTICES_PER_PAGE , $since_id = 0 , $before_id = 0 )
{
return Reply :: stream ( $this -> getID (), $offset , $limit , $since_id , $before_id );
}
2010-03-02 19:54:02 +00:00
function getTaggedNotices ( $tag , $offset = 0 , $limit = NOTICES_PER_PAGE , $since_id = 0 , $max_id = 0 )
2009-05-18 22:18:57 +01:00
{
2011-03-24 22:04:19 +00:00
$stream = new TaggedProfileNoticeStream ( $this , $tag );
2009-06-17 23:04:57 +01:00
2011-03-23 15:29:55 +00:00
return $stream -> getNotices ( $offset , $limit , $since_id , $max_id );
2009-05-18 22:18:57 +01:00
}
2013-10-06 20:30:29 +01:00
function getNotices ( $offset = 0 , $limit = NOTICES_PER_PAGE , $since_id = 0 , $max_id = 0 , Profile $scoped = null )
2009-05-01 19:27:57 +01:00
{
2013-10-06 20:30:29 +01:00
$stream = new ProfileNoticeStream ( $this , $scoped );
2010-12-17 21:20:38 +00:00
2011-03-23 15:29:55 +00:00
return $stream -> getNotices ( $offset , $limit , $since_id , $max_id );
2008-12-23 19:19:07 +00:00
}
2009-01-21 18:57:18 +00:00
2014-03-01 10:55:06 +00:00
function isMember ( User_group $group )
2009-01-21 18:57:18 +00:00
{
2011-08-02 17:14:55 +01:00
$groups = $this -> getGroups ( 0 , null );
2013-10-29 10:27:26 +00:00
while ( $groups instanceof User_group && $groups -> fetch ()) {
if ( $groups -> id == $group -> id ) {
2011-08-02 17:14:55 +01:00
return true ;
}
}
return false ;
2009-01-21 18:57:18 +00:00
}
2014-03-01 10:55:06 +00:00
function isAdmin ( User_group $group )
2009-01-21 18:57:18 +00:00
{
2011-04-06 23:12:25 +01:00
$gm = Group_member :: pkeyGet ( array ( 'profile_id' => $this -> id ,
'group_id' => $group -> id ));
return ( ! empty ( $gm ) && $gm -> is_admin );
2009-01-21 18:57:18 +00:00
}
2011-03-21 23:26:41 +00:00
function isPendingMember ( $group )
{
$request = Group_join_queue :: pkeyGet ( array ( 'profile_id' => $this -> id ,
'group_id' => $group -> id ));
return ! empty ( $request );
}
2011-04-05 22:20:17 +01:00
function getGroups ( $offset = 0 , $limit = PROFILES_PER_PAGE )
2010-03-03 19:00:02 +00:00
{
2011-04-05 22:20:17 +01:00
$ids = array ();
2011-04-10 18:59:55 +01:00
2011-04-05 22:20:17 +01:00
$keypart = sprintf ( 'profile:groups:%d' , $this -> id );
$idstring = self :: cacheGet ( $keypart );
if ( $idstring !== false ) {
$ids = explode ( ',' , $idstring );
} else {
$gm = new Group_member ();
$gm -> profile_id = $this -> id ;
if ( $gm -> find ()) {
while ( $gm -> fetch ()) {
$ids [] = $gm -> group_id ;
2010-03-03 19:00:02 +00:00
}
}
2011-04-05 22:20:17 +01:00
self :: cacheSet ( $keypart , implode ( ',' , $ids ));
2010-03-03 19:00:02 +00:00
}
2011-09-30 16:38:06 +01:00
if ( ! is_null ( $offset ) && ! is_null ( $limit )) {
$ids = array_slice ( $ids , $offset , $limit );
}
2013-10-29 10:27:26 +00:00
try {
2014-10-25 16:57:20 +01:00
return User_group :: multiGet ( 'id' , $ids );
2013-10-29 10:27:26 +00:00
} catch ( NoResultException $e ) {
return null ; // throw exception when we handle it everywhere
}
}
function getGroupCount () {
$groups = $this -> getGroups ( 0 , null );
return $groups instanceof User_group
? $groups -> N
: 0 ;
2010-03-03 19:00:02 +00:00
}
2011-03-06 17:58:03 +00:00
function isTagged ( $peopletag )
{
$tag = Profile_tag :: pkeyGet ( array ( 'tagger' => $peopletag -> tagger ,
'tagged' => $this -> id ,
'tag' => $peopletag -> tag ));
return ! empty ( $tag );
}
function canTag ( $tagged )
{
if ( empty ( $tagged )) {
return false ;
}
if ( $tagged -> id == $this -> id ) {
return true ;
}
$all = common_config ( 'peopletag' , 'allow_tagging' , 'all' );
$local = common_config ( 'peopletag' , 'allow_tagging' , 'local' );
$remote = common_config ( 'peopletag' , 'allow_tagging' , 'remote' );
$subs = common_config ( 'peopletag' , 'allow_tagging' , 'subs' );
if ( $all ) {
return true ;
}
$tagged_user = $tagged -> getUser ();
if ( ! empty ( $tagged_user )) {
if ( $local ) {
return true ;
}
} else if ( $subs ) {
return ( Subscription :: exists ( $this , $tagged ) ||
Subscription :: exists ( $tagged , $this ));
} else if ( $remote ) {
return true ;
}
return false ;
}
2016-01-05 23:18:10 +00:00
function getLists ( Profile $scoped = null , $offset = 0 , $limit = null , $since_id = 0 , $max_id = 0 )
2011-03-06 17:58:03 +00:00
{
2011-04-15 13:51:47 +01:00
$ids = array ();
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
$keypart = sprintf ( 'profile:lists:%d' , $this -> id );
$idstr = self :: cacheGet ( $keypart );
if ( $idstr !== false ) {
$ids = explode ( ',' , $idstr );
2011-03-06 17:58:03 +00:00
} else {
2011-04-15 13:51:47 +01:00
$list = new Profile_list ();
$list -> selectAdd ();
$list -> selectAdd ( 'id' );
$list -> tagger = $this -> id ;
$list -> selectAdd ( 'id as "cursor"' );
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
if ( $since_id > 0 ) {
$list -> whereAdd ( 'id > ' . $since_id );
}
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
if ( $max_id > 0 ) {
$list -> whereAdd ( 'id <= ' . $max_id );
}
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
if ( $offset >= 0 && ! is_null ( $limit )) {
$list -> limit ( $offset , $limit );
}
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
$list -> orderBy ( 'id DESC' );
if ( $list -> find ()) {
while ( $list -> fetch ()) {
$ids [] = $list -> id ;
}
}
self :: cacheSet ( $keypart , implode ( ',' , $ids ));
2011-03-06 17:58:03 +00:00
}
2016-01-05 23:18:10 +00:00
$showPrivate = $this -> sameAs ( $scoped );
2011-03-06 17:58:03 +00:00
2011-04-15 13:51:47 +01:00
$lists = array ();
foreach ( $ids as $id ) {
2013-08-18 12:04:58 +01:00
$list = Profile_list :: getKV ( 'id' , $id );
2011-04-15 13:51:47 +01:00
if ( ! empty ( $list ) &&
( $showPrivate || ! $list -> private )) {
if ( ! isset ( $list -> cursor )) {
$list -> cursor = $list -> id ;
}
$lists [] = $list ;
}
}
return new ArrayWrapper ( $lists );
2011-03-06 17:58:03 +00:00
}
2011-09-27 15:51:02 +01:00
/**
* Get tags that other people put on this profile , in reverse - chron order
*
2016-01-05 22:07:30 +00:00
* @ param Profile $scoped User we are requesting as
2011-09-27 15:51:02 +01:00
* @ param int $offset Offset from latest
* @ param int $limit Max number to get
* @ param datetime $since_id max date
* @ param datetime $max_id min date
*
* @ return Profile_list resulting lists
*/
2016-01-05 22:07:30 +00:00
function getOtherTags ( Profile $scoped = null , $offset = 0 , $limit = null , $since_id = 0 , $max_id = 0 )
2011-03-06 17:58:03 +00:00
{
2011-09-27 15:51:02 +01:00
$list = new Profile_list ();
2011-03-06 17:58:03 +00:00
2011-09-27 15:51:02 +01:00
$qry = sprintf ( 'select profile_list.*, unix_timestamp(profile_tag.modified) as "cursor" ' .
'from profile_tag join profile_list ' .
'on (profile_tag.tagger = profile_list.tagger ' .
' and profile_tag.tag = profile_list.tag) ' .
'where profile_tag.tagged = %d ' ,
$this -> id );
2011-08-26 16:48:40 +01:00
2011-03-06 17:58:03 +00:00
2016-01-05 22:07:30 +00:00
if ( ! is_null ( $scoped )) {
2011-09-27 15:51:02 +01:00
$qry .= sprintf ( 'AND ( ( profile_list.private = false ) ' .
'OR ( profile_list.tagger = %d AND ' .
'profile_list.private = true ) )' ,
2016-01-05 22:07:30 +00:00
$scoped -> getID ());
2011-03-06 17:58:03 +00:00
} else {
2011-09-27 15:51:02 +01:00
$qry .= 'AND profile_list.private = 0 ' ;
2011-03-06 17:58:03 +00:00
}
2011-09-27 15:51:02 +01:00
if ( $since_id > 0 ) {
$qry .= sprintf ( 'AND (cursor > %d) ' , $since_id );
2011-03-06 17:58:03 +00:00
}
2011-09-27 15:51:02 +01:00
if ( $max_id > 0 ) {
$qry .= sprintf ( 'AND (cursor < %d) ' , $max_id );
2011-03-06 17:58:03 +00:00
}
2011-09-27 15:51:02 +01:00
$qry .= 'ORDER BY profile_tag.modified DESC ' ;
2011-03-06 17:58:03 +00:00
2011-09-27 15:51:02 +01:00
if ( $offset >= 0 && ! is_null ( $limit )) {
$qry .= sprintf ( 'LIMIT %d OFFSET %d ' , $limit , $offset );
}
2011-03-06 17:58:03 +00:00
2011-09-27 15:51:02 +01:00
$list -> query ( $qry );
return $list ;
2011-03-06 17:58:03 +00:00
}
function getPrivateTags ( $offset = 0 , $limit = null , $since_id = 0 , $max_id = 0 )
{
$tags = new Profile_list ();
$tags -> private = true ;
$tags -> tagger = $this -> id ;
if ( $since_id > 0 ) {
$tags -> whereAdd ( 'id > ' . $since_id );
}
if ( $max_id > 0 ) {
$tags -> whereAdd ( 'id <= ' . $max_id );
}
if ( $offset >= 0 && ! is_null ( $limit )) {
$tags -> limit ( $offset , $limit );
}
$tags -> orderBy ( 'id DESC' );
$tags -> find ();
return $tags ;
}
function hasLocalTags ()
{
$tags = new Profile_tag ();
$tags -> joinAdd ( array ( 'tagger' , 'user:id' ));
$tags -> whereAdd ( 'tagged = ' . $this -> id );
$tags -> whereAdd ( 'tagger != ' . $this -> id );
$tags -> limit ( 0 , 1 );
$tags -> fetch ();
return ( $tags -> N == 0 ) ? false : true ;
}
function getTagSubscriptions ( $offset = 0 , $limit = null , $since_id = 0 , $max_id = 0 )
{
$lists = new Profile_list ();
$subs = new Profile_tag_subscription ();
2013-09-16 21:36:44 +01:00
$lists -> joinAdd ( array ( 'id' , 'profile_tag_subscription:profile_tag_id' ));
2011-08-26 16:48:40 +01:00
2011-03-06 17:58:03 +00:00
#@fixme: postgres (round(date_part('epoch', my_date)))
$lists -> selectAdd ( 'unix_timestamp(profile_tag_subscription.created) as "cursor"' );
$lists -> whereAdd ( 'profile_tag_subscription.profile_id = ' . $this -> id );
if ( $since_id > 0 ) {
$lists -> whereAdd ( 'cursor > ' . $since_id );
}
if ( $max_id > 0 ) {
$lists -> whereAdd ( 'cursor <= ' . $max_id );
}
if ( $offset >= 0 && ! is_null ( $limit )) {
$lists -> limit ( $offset , $limit );
}
$lists -> orderBy ( '"cursor" DESC' );
$lists -> find ();
return $lists ;
}
2011-03-21 21:35:29 +00:00
/**
* Request to join the given group .
* May throw exceptions on failure .
*
* @ param User_group $group
2011-03-21 22:04:32 +00:00
* @ return mixed : Group_member on success , Group_join_queue if pending approval , null on some cancels ?
2011-03-21 21:35:29 +00:00
*/
function joinGroup ( User_group $group )
{
2011-03-22 23:26:26 +00:00
$join = null ;
2011-03-21 22:04:32 +00:00
if ( $group -> join_policy == User_group :: JOIN_POLICY_MODERATE ) {
2011-03-22 23:26:26 +00:00
$join = Group_join_queue :: saveNew ( $this , $group );
2011-03-21 22:04:32 +00:00
} else {
if ( Event :: handle ( 'StartJoinGroup' , array ( $group , $this ))) {
2011-03-22 23:26:26 +00:00
$join = Group_member :: join ( $group -> id , $this -> id );
2011-04-05 22:20:17 +01:00
self :: blow ( 'profile:groups:%d' , $this -> id );
2012-07-04 19:39:26 +01:00
self :: blow ( 'group:member_ids:%d' , $group -> id );
2012-07-04 19:11:42 +01:00
self :: blow ( 'group:member_count:%d' , $group -> id );
2011-03-21 22:04:32 +00:00
Event :: handle ( 'EndJoinGroup' , array ( $group , $this ));
}
2011-03-21 21:35:29 +00:00
}
2011-03-22 23:26:26 +00:00
if ( $join ) {
// Send any applicable notifications...
$join -> notify ();
2011-03-22 00:17:18 +00:00
}
2011-03-22 23:26:26 +00:00
return $join ;
2011-03-22 00:17:18 +00:00
}
2011-03-21 21:35:29 +00:00
/**
* Leave a group that this profile is a member of .
*
2011-03-24 08:59:01 +00:00
* @ param User_group $group
2011-03-21 21:35:29 +00:00
*/
function leaveGroup ( User_group $group )
{
2011-03-21 22:04:32 +00:00
if ( Event :: handle ( 'StartLeaveGroup' , array ( $group , $this ))) {
Group_member :: leave ( $group -> id , $this -> id );
2011-04-05 22:20:17 +01:00
self :: blow ( 'profile:groups:%d' , $this -> id );
2012-07-04 19:39:26 +01:00
self :: blow ( 'group:member_ids:%d' , $group -> id );
2012-07-04 19:11:42 +01:00
self :: blow ( 'group:member_count:%d' , $group -> id );
2011-03-21 22:04:32 +00:00
Event :: handle ( 'EndLeaveGroup' , array ( $group , $this ));
2011-03-21 21:35:29 +00:00
}
}
2009-02-06 08:13:08 +00:00
function avatarUrl ( $size = AVATAR_PROFILE_SIZE )
{
2013-10-06 14:54:06 +01:00
return Avatar :: urlByProfile ( $this , $size );
2009-02-06 08:13:08 +00:00
}
2009-06-26 07:00:46 +01:00
2013-09-21 17:26:58 +01:00
function getSubscribed ( $offset = 0 , $limit = null )
2009-06-26 07:00:46 +01:00
{
2013-09-21 17:26:58 +01:00
$subs = Subscription :: getSubscribedIDs ( $this -> id , $offset , $limit );
2013-10-07 23:21:24 +01:00
try {
2014-10-25 16:57:20 +01:00
$profiles = Profile :: multiGet ( 'id' , $subs );
2013-10-07 23:21:24 +01:00
} catch ( NoResultException $e ) {
return $e -> obj ;
}
2013-09-21 17:26:58 +01:00
return $profiles ;
2009-06-26 07:00:46 +01:00
}
function getSubscribers ( $offset = 0 , $limit = null )
{
2013-09-21 17:26:58 +01:00
$subs = Subscription :: getSubscriberIDs ( $this -> id , $offset , $limit );
2013-10-07 23:21:24 +01:00
try {
2014-10-25 16:57:20 +01:00
$profiles = Profile :: multiGet ( 'id' , $subs );
2013-10-07 23:21:24 +01:00
} catch ( NoResultException $e ) {
return $e -> obj ;
}
2013-09-21 17:26:58 +01:00
return $profiles ;
2009-11-13 03:42:18 +00:00
}
2013-10-15 01:00:27 +01:00
function getTaggedSubscribers ( $tag , $offset = 0 , $limit = null )
2011-03-06 17:58:03 +00:00
{
$qry =
'SELECT profile.* ' .
2013-10-15 01:00:27 +01:00
'FROM profile JOIN subscription ' .
2011-03-06 17:58:03 +00:00
'ON profile.id = subscription.subscriber ' .
2013-10-15 01:00:27 +01:00
'JOIN profile_tag ON (profile_tag.tagged = subscription.subscriber ' .
'AND profile_tag.tagger = subscription.subscribed) ' .
2011-03-06 17:58:03 +00:00
'WHERE subscription.subscribed = %d ' .
2013-10-15 01:00:27 +01:00
" AND profile_tag.tag = '%s' " .
2011-03-06 17:58:03 +00:00
'AND subscription.subscribed != subscription.subscriber ' .
2013-10-15 01:00:27 +01:00
'ORDER BY subscription.created DESC ' ;
if ( $offset ) {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset ;
}
2011-03-06 17:58:03 +00:00
$profile = new Profile ();
2013-10-15 01:00:27 +01:00
$cnt = $profile -> query ( sprintf ( $qry , $this -> id , $profile -> escape ( $tag )));
2011-03-06 17:58:03 +00:00
2013-10-15 01:00:27 +01:00
return $profile ;
}
function getTaggedSubscriptions ( $tag , $offset = 0 , $limit = null )
{
$qry =
'SELECT profile.* ' .
'FROM profile JOIN subscription ' .
'ON profile.id = subscription.subscribed ' .
'JOIN profile_tag on (profile_tag.tagged = subscription.subscribed ' .
'AND profile_tag.tagger = subscription.subscriber) ' .
'WHERE subscription.subscriber = %d ' .
" AND profile_tag.tag = '%s' " .
'AND subscription.subscribed != subscription.subscriber ' .
'ORDER BY subscription.created DESC ' ;
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset ;
$profile = new Profile ();
$profile -> query ( sprintf ( $qry , $this -> id , $profile -> escape ( $tag )));
return $profile ;
2011-03-06 17:58:03 +00:00
}
2011-03-29 01:06:02 +01:00
/**
* Get pending subscribers , who have not yet been approved .
*
* @ param int $offset
* @ param int $limit
* @ return Profile
*/
function getRequests ( $offset = 0 , $limit = null )
{
2016-01-03 19:27:53 +00:00
// FIXME: mysql only
$subqueue = new Profile ();
$subqueue -> joinAdd ( array ( 'id' , 'subscription_queue:subscriber' ));
$subqueue -> whereAdd ( sprintf ( 'subscription_queue.subscribed = %d' , $this -> getID ()));
$subqueue -> limit ( $offset , $limit );
$subqueue -> orderBy ( 'subscription_queue.created' , 'DESC' );
if ( ! $subqueue -> find ()) {
throw new NoResultException ( $subqueue );
}
return $subqueue ;
2011-03-29 01:06:02 +01:00
}
2009-07-10 01:28:38 +01:00
function subscriptionCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$cnt = $c -> get ( Cache :: key ( 'profile:subscription_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
if ( is_integer ( $cnt )) {
return ( int ) $cnt ;
}
}
$sub = new Subscription ();
$sub -> subscriber = $this -> id ;
$cnt = ( int ) $sub -> count ( 'distinct subscribed' );
$cnt = ( $cnt > 0 ) ? $cnt - 1 : $cnt ;
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> set ( Cache :: key ( 'profile:subscription_count:' . $this -> id ), $cnt );
2009-07-10 01:28:38 +01:00
}
return $cnt ;
}
function subscriberCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$cnt = $c -> get ( Cache :: key ( 'profile:subscriber_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
if ( is_integer ( $cnt )) {
return ( int ) $cnt ;
}
}
$sub = new Subscription ();
$sub -> subscribed = $this -> id ;
2010-08-06 18:56:18 +01:00
$sub -> whereAdd ( 'subscriber != subscribed' );
2009-07-10 01:28:38 +01:00
$cnt = ( int ) $sub -> count ( 'distinct subscriber' );
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> set ( Cache :: key ( 'profile:subscriber_count:' . $this -> id ), $cnt );
2009-07-10 01:28:38 +01:00
}
return $cnt ;
}
2010-11-15 23:32:57 +00:00
/**
* Is this profile subscribed to another profile ?
*
* @ param Profile $other
* @ return boolean
*/
2014-03-05 12:44:34 +00:00
function isSubscribed ( Profile $other )
2010-11-15 23:32:57 +00:00
{
return Subscription :: exists ( $this , $other );
}
2016-01-03 18:05:49 +00:00
function readableBy ( Profile $other = null )
{
// If it's not a private stream, it's readable by anyone
if ( ! $this -> isPrivateStream ()) {
return true ;
}
// If it's a private stream, $other must be a subscriber to $this
return is_null ( $other ) ? false : $other -> isSubscribed ( $this );
}
2016-01-03 18:33:26 +00:00
function requiresSubscriptionApproval ( Profile $other = null )
{
if ( ! $this -> isLocal ()) {
// We don't know for remote users, and we'll always be able to send
// the request. Whether it'll work immediately or require moderation
// can be determined in another function.
return false ;
}
// Assume that profiles _we_ subscribe to are permitted. Could be made configurable.
if ( ! is_null ( $other ) && $this -> isSubscribed ( $other )) {
return false ;
}
// If the local user either has a private stream (implies the following)
// or user has a moderation policy for new subscriptions, return true.
return $this -> getUser () -> private_stream || $this -> getUser () -> subscribe_policy === User :: SUBSCRIBE_POLICY_MODERATE ;
}
2011-03-29 00:12:51 +01:00
/**
* Check if a pending subscription request is outstanding for this ...
*
* @ param Profile $other
* @ return boolean
*/
2014-04-29 19:37:58 +01:00
function hasPendingSubscription ( Profile $other )
2011-03-29 00:12:51 +01:00
{
return Subscription_queue :: exists ( $this , $other );
}
2010-11-15 23:32:57 +00:00
/**
* Are these two profiles subscribed to each other ?
*
* @ param Profile $other
* @ return boolean
*/
2014-04-29 19:37:58 +01:00
function mutuallySubscribed ( Profile $other )
2010-11-15 23:32:57 +00:00
{
return $this -> isSubscribed ( $other ) &&
$other -> isSubscribed ( $this );
}
2009-07-10 01:28:38 +01:00
function noticeCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$cnt = $c -> get ( Cache :: key ( 'profile:notice_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
if ( is_integer ( $cnt )) {
return ( int ) $cnt ;
}
}
$notices = new Notice ();
$notices -> profile_id = $this -> id ;
2016-01-05 23:29:48 +00:00
$notices -> verb = ActivityVerb :: POST ;
2009-07-10 01:28:38 +01:00
$cnt = ( int ) $notices -> count ( 'distinct id' );
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> set ( Cache :: key ( 'profile:notice_count:' . $this -> id ), $cnt );
2009-07-10 01:28:38 +01:00
}
return $cnt ;
}
function blowSubscriberCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> delete ( Cache :: key ( 'profile:subscriber_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
}
}
function blowSubscriptionCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> delete ( Cache :: key ( 'profile:subscription_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
}
}
function blowNoticeCount ()
{
2010-09-06 14:56:45 +01:00
$c = Cache :: instance ();
2009-07-10 01:28:38 +01:00
if ( ! empty ( $c )) {
2010-09-06 15:07:43 +01:00
$c -> delete ( Cache :: key ( 'profile:notice_count:' . $this -> id ));
2009-07-10 01:28:38 +01:00
}
}
2009-08-21 12:40:46 +01:00
static function maxBio ()
{
2009-08-21 12:48:14 +01:00
$biolimit = common_config ( 'profile' , 'biolimit' );
2009-08-21 12:40:46 +01:00
// null => use global limit (distinct from 0!)
if ( is_null ( $biolimit )) {
$biolimit = common_config ( 'site' , 'textlimit' );
}
return $biolimit ;
}
static function bioTooLong ( $bio )
{
$biolimit = self :: maxBio ();
return ( $biolimit > 0 && ! empty ( $bio ) && ( mb_strlen ( $bio ) > $biolimit ));
}
2009-10-04 08:07:37 +01:00
2013-10-28 18:36:05 +00:00
function update ( $dataObject = false )
2013-10-17 15:38:42 +01:00
{
2013-10-28 18:36:05 +00:00
if ( is_object ( $dataObject ) && $this -> nickname != $dataObject -> nickname ) {
try {
$local = $this -> getUser ();
common_debug ( " Updating User ( { $this -> id } ) nickname from { $dataObject -> nickname } to { $this -> nickname } " );
2013-10-17 15:38:42 +01:00
$origuser = clone ( $local );
$local -> nickname = $this -> nickname ;
2015-01-25 11:45:26 +00:00
// updateWithKeys throws exception on failure.
$local -> updateWithKeys ( $origuser );
2013-10-17 15:38:42 +01:00
// Clear the site owner, in case nickname changed
if ( $local -> hasRole ( Profile_role :: OWNER )) {
User :: blow ( 'user:site_owner' );
}
2013-10-28 18:36:05 +00:00
} catch ( NoSuchUserException $e ) {
// Nevermind...
2013-10-17 15:38:42 +01:00
}
}
2013-10-28 18:36:05 +00:00
return parent :: update ( $dataObject );
2013-10-17 15:38:42 +01:00
}
2013-10-29 09:20:57 +00:00
function delete ( $useWhere = false )
2009-07-26 20:06:38 +01:00
{
2015-07-18 01:16:52 +01:00
// just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
if ( ! $this -> hasRole ( Profile_role :: DELETED )) {
$this -> grantRole ( Profile_role :: DELETED );
}
2009-07-26 20:06:38 +01:00
$this -> _deleteNotices ();
$this -> _deleteSubscriptions ();
$this -> _deleteTags ();
$this -> _deleteBlocks ();
2014-03-06 13:43:36 +00:00
$this -> _deleteAttentions ();
2013-10-01 10:37:59 +01:00
Avatar :: deleteFromProfile ( $this , true );
2009-07-26 20:06:38 +01:00
2010-11-19 20:39:07 +00:00
// Warning: delete() will run on the batch objects,
// not on individual objects.
$related = array ( 'Reply' ,
2009-07-26 20:06:38 +01:00
'Group_member' ,
);
2010-01-06 19:10:33 +00:00
Event :: handle ( 'ProfileDeleteRelated' , array ( $this , & $related ));
2009-07-26 20:06:38 +01:00
foreach ( $related as $cls ) {
$inst = new $cls ();
$inst -> profile_id = $this -> id ;
$inst -> delete ();
}
2015-05-30 15:40:00 +01:00
$localuser = User :: getKV ( 'id' , $this -> id );
if ( $localuser instanceof User ) {
$localuser -> delete ();
}
2013-10-29 09:20:57 +00:00
return parent :: delete ( $useWhere );
2009-07-26 20:06:38 +01:00
}
function _deleteNotices ()
{
$notice = new Notice ();
$notice -> profile_id = $this -> id ;
if ( $notice -> find ()) {
while ( $notice -> fetch ()) {
$other = clone ( $notice );
$other -> delete ();
}
}
}
function _deleteSubscriptions ()
{
$sub = new Subscription ();
2016-01-03 21:21:03 +00:00
$sub -> subscriber = $this -> getID ();
2010-03-31 20:02:19 +01:00
$sub -> find ();
while ( $sub -> fetch ()) {
2016-01-03 21:21:03 +00:00
try {
$other = $sub -> getSubscribed ();
if ( ! $other -> sameAs ( $this )) {
Subscription :: cancel ( $this , $other );
}
} catch ( NoResultException $e ) {
// Profile not found
common_log ( LOG_INFO , 'Subscribed profile id==' . $sub -> subscribed . ' not found when deleting profile id==' . $this -> getID () . ', ignoring...' );
} catch ( ServerException $e ) {
// Subscription cancel failed
2016-01-03 21:22:58 +00:00
common_log ( LOG_INFO , 'Subscribed profile id==' . $other -> getID () . ' could not be reached for unsubscription notice when deleting profile id==' . $this -> getID () . ', ignoring...' );
2010-03-31 20:02:19 +01:00
}
}
2009-07-26 20:06:38 +01:00
2016-01-03 21:21:03 +00:00
$sub = new Subscription ();
$sub -> subscribed = $this -> getID ();
$sub -> find ();
2010-03-31 20:02:19 +01:00
2016-01-03 21:21:03 +00:00
while ( $sub -> fetch ()) {
try {
$other = $sub -> getSubscriber ();
common_log ( LOG_INFO , 'Subscriber profile id==' . $sub -> subscribed . ' not found when deleting profile id==' . $this -> getID () . ', ignoring...' );
if ( ! $other -> sameAs ( $this )) {
Subscription :: cancel ( $other , $this );
}
} catch ( NoResultException $e ) {
// Profile not found
common_log ( LOG_INFO , 'Subscribed profile id==' . $sub -> subscribed . ' not found when deleting profile id==' . $this -> getID () . ', ignoring...' );
} catch ( ServerException $e ) {
// Subscription cancel failed
2016-01-03 21:22:58 +00:00
common_log ( LOG_INFO , 'Subscriber profile id==' . $other -> getID () . ' could not be reached for unsubscription notice when deleting profile id==' . $this -> getID () . ', ignoring...' );
2010-03-31 20:02:19 +01:00
}
}
2016-01-03 21:21:03 +00:00
// Finally delete self-subscription
2010-03-31 20:02:19 +01:00
$self = new Subscription ();
2016-01-03 21:21:03 +00:00
$self -> subscriber = $this -> getID ();
$self -> subscribed = $this -> getID ();
2010-03-31 20:02:19 +01:00
$self -> delete ();
2009-07-26 20:06:38 +01:00
}
function _deleteTags ()
{
$tag = new Profile_tag ();
$tag -> tagged = $this -> id ;
2009-10-02 20:42:34 +01:00
$tag -> delete ();
2009-07-26 20:06:38 +01:00
}
function _deleteBlocks ()
{
$block = new Profile_block ();
$block -> blocked = $this -> id ;
$block -> delete ();
$block = new Group_block ();
$block -> blocked = $this -> id ;
$block -> delete ();
}
2009-10-29 18:43:25 +00:00
2014-03-06 13:43:36 +00:00
function _deleteAttentions ()
{
$att = new Attention ();
$att -> profile_id = $this -> getID ();
if ( $att -> find ()) {
while ( $att -> fetch ()) {
// Can't do delete() on the object directly since it won't remove all of it
$other = clone ( $att );
$other -> delete ();
}
}
}
2009-10-29 18:43:25 +00:00
// XXX: identical to Notice::getLocation.
2013-10-06 12:38:09 +01:00
public function getLocation ()
2009-10-29 18:43:25 +00:00
{
$location = null ;
if ( ! empty ( $this -> location_id ) && ! empty ( $this -> location_ns )) {
$location = Location :: fromId ( $this -> location_id , $this -> location_ns );
}
if ( is_null ( $location )) { // no ID, or Location::fromId() failed
if ( ! empty ( $this -> lat ) && ! empty ( $this -> lon )) {
$location = Location :: fromLatLon ( $this -> lat , $this -> lon );
}
}
if ( is_null ( $location )) { // still haven't found it!
if ( ! empty ( $this -> location )) {
$location = Location :: fromName ( $this -> location );
}
}
return $location ;
}
2009-11-16 14:52:33 +00:00
2013-10-06 12:38:09 +01:00
public function shareLocation ()
{
$cfg = common_config ( 'location' , 'share' );
if ( $cfg == 'always' ) {
return true ;
} else if ( $cfg == 'never' ) {
return false ;
} else { // user
$share = common_config ( 'location' , 'sharedefault' );
// Check if user has a personal setting for this
$prefs = User_location_prefs :: getKV ( 'user_id' , $this -> id );
if ( ! empty ( $prefs )) {
$share = $prefs -> share_location ;
$prefs -> free ();
}
return $share ;
}
}
2009-11-16 14:52:33 +00:00
function hasRole ( $name )
{
2009-11-18 19:19:43 +00:00
$has_role = false ;
if ( Event :: handle ( 'StartHasRole' , array ( $this , $name , & $has_role ))) {
$role = Profile_role :: pkeyGet ( array ( 'profile_id' => $this -> id ,
'role' => $name ));
$has_role = ! empty ( $role );
Event :: handle ( 'EndHasRole' , array ( $this , $name , $has_role ));
}
return $has_role ;
2009-11-16 14:52:33 +00:00
}
function grantRole ( $name )
{
2010-10-22 15:31:50 +01:00
if ( Event :: handle ( 'StartGrantRole' , array ( $this , $name ))) {
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
$role = new Profile_role ();
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
$role -> profile_id = $this -> id ;
$role -> role = $name ;
$role -> created = common_sql_now ();
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
$result = $role -> insert ();
if ( ! $result ) {
throw new Exception ( " Can't save role ' $name ' for profile ' { $this -> id } ' " );
}
2011-02-18 00:46:08 +00:00
if ( $name == 'owner' ) {
User :: blow ( 'user:site_owner' );
}
2010-10-22 15:31:50 +01:00
Event :: handle ( 'EndGrantRole' , array ( $this , $name ));
2009-11-16 14:52:33 +00:00
}
2010-10-22 15:31:50 +01:00
return $result ;
2009-11-16 14:52:33 +00:00
}
function revokeRole ( $name )
{
2010-10-22 15:31:50 +01:00
if ( Event :: handle ( 'StartRevokeRole' , array ( $this , $name ))) {
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
$role = Profile_role :: pkeyGet ( array ( 'profile_id' => $this -> id ,
'role' => $name ));
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
if ( empty ( $role )) {
// TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
throw new Exception ( sprintf ( _ ( 'Cannot revoke role "%1$s" for user #%2$d; does not exist.' ), $name , $this -> id ));
}
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
$result = $role -> delete ();
2009-11-16 14:52:33 +00:00
2010-10-22 15:31:50 +01:00
if ( ! $result ) {
common_log_db_error ( $role , 'DELETE' , __FILE__ );
// TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
// TRANS: %1$s is the role name, %2$s is the user ID (number).
throw new Exception ( sprintf ( _ ( 'Cannot revoke role "%1$s" for user #%2$d; database error.' ), $name , $this -> id ));
}
2011-02-18 00:46:08 +00:00
if ( $name == 'owner' ) {
User :: blow ( 'user:site_owner' );
}
2010-10-22 15:31:50 +01:00
Event :: handle ( 'EndRevokeRole' , array ( $this , $name ));
return true ;
}
2009-11-16 14:52:33 +00:00
}
function isSandboxed ()
{
2009-11-16 15:06:52 +00:00
return $this -> hasRole ( Profile_role :: SANDBOXED );
2009-11-16 14:52:33 +00:00
}
function isSilenced ()
{
2009-11-16 15:06:52 +00:00
return $this -> hasRole ( Profile_role :: SILENCED );
2009-11-16 14:52:33 +00:00
}
function sandbox ()
{
2009-11-16 15:06:52 +00:00
$this -> grantRole ( Profile_role :: SANDBOXED );
2009-11-16 14:52:33 +00:00
}
function unsandbox ()
{
2009-11-16 15:06:52 +00:00
$this -> revokeRole ( Profile_role :: SANDBOXED );
2009-11-16 14:52:33 +00:00
}
function silence ()
{
2009-11-16 15:06:52 +00:00
$this -> grantRole ( Profile_role :: SILENCED );
2012-03-22 15:37:45 +00:00
if ( common_config ( 'notice' , 'hidespam' )) {
$this -> flushVisibility ();
}
2009-11-16 14:52:33 +00:00
}
2016-02-12 13:22:25 +00:00
function silenceAs ( Profile $actor )
{
if ( ! $actor -> hasRight ( Right :: SILENCEUSER )) {
throw new AuthorizationException ( _ ( 'You cannot silence users on this site.' ));
}
// Only administrators can silence other priviliged users (those who have the right to silence as well).
if ( $this -> hasRight ( Right :: SILENCEUSER ) && ! $actor -> hasRole ( Profile_role :: ADMINISTRATOR )) {
throw new AuthorizationException ( _ ( 'You cannot silence other priviliged users.' ));
}
if ( $this -> isSilenced ()) {
// TRANS: Client error displayed trying to silence an already silenced user.
throw new AlreadyFulfilledException ( _ ( 'User is already silenced.' ));
}
return $this -> silence ();
}
2009-11-16 14:52:33 +00:00
function unsilence ()
{
2009-11-16 15:06:52 +00:00
$this -> revokeRole ( Profile_role :: SILENCED );
2012-03-22 15:37:45 +00:00
if ( common_config ( 'notice' , 'hidespam' )) {
$this -> flushVisibility ();
}
}
2016-02-12 13:22:25 +00:00
function unsilenceAs ( Profile $actor )
{
if ( ! $actor -> hasRight ( Right :: SILENCEUSER )) {
// TRANS: Client error displayed trying to unsilence a user when the user does not have the right.
throw new AuthorizationException ( _ ( 'You cannot unsilence users on this site.' ));
}
if ( ! $this -> isSilenced ()) {
// TRANS: Client error displayed trying to unsilence a user when the target user has not been silenced.
throw new AlreadyFulfilledException ( _ ( 'User is not silenced.' ));
}
return $this -> unsilence ();
}
2012-03-22 15:37:45 +00:00
function flushVisibility ()
{
// Get all notices
$stream = new ProfileNoticeStream ( $this , $this );
$ids = $stream -> getNoticeIds ( 0 , CachingNoticeStream :: CACHE_WINDOW );
foreach ( $ids as $id ) {
self :: blow ( 'notice:in-scope-for:%d:null' , $id );
}
2009-11-16 14:52:33 +00:00
}
2009-11-16 18:03:59 +00:00
/**
* Does this user have the right to do X ?
*
* With our role - based authorization , this is merely a lookup for whether the user
* has a particular role . The implementation currently uses a switch statement
* to determine if the user has the pre - defined role to exercise the right . Future
* implementations may allow per - site roles , and different mappings of roles to rights .
*
* @ param $right string Name of the right , usually a constant in class Right
* @ return boolean whether the user has the right in question
*/
2013-10-08 20:08:02 +01:00
public function hasRight ( $right )
2009-11-16 18:03:59 +00:00
{
$result = false ;
2010-09-28 22:42:18 +01:00
2010-03-15 22:08:16 +00:00
if ( $this -> hasRole ( Profile_role :: DELETED )) {
return false ;
}
2010-09-28 22:42:18 +01:00
2009-11-16 18:03:59 +00:00
if ( Event :: handle ( 'UserRightsCheck' , array ( $this , $right , & $result ))) {
switch ( $right )
{
case Right :: DELETEOTHERSNOTICE :
2010-02-06 10:36:59 +00:00
case Right :: MAKEGROUPADMIN :
2009-11-16 18:03:59 +00:00
case Right :: SANDBOXUSER :
case Right :: SILENCEUSER :
case Right :: DELETEUSER :
2010-10-12 23:49:20 +01:00
case Right :: DELETEGROUP :
2012-03-25 18:16:23 +01:00
case Right :: TRAINSPAM :
case Right :: REVIEWSPAM :
2009-11-16 18:03:59 +00:00
$result = $this -> hasRole ( Profile_role :: MODERATOR );
break ;
case Right :: CONFIGURESITE :
$result = $this -> hasRole ( Profile_role :: ADMINISTRATOR );
break ;
2010-03-03 23:43:49 +00:00
case Right :: GRANTROLE :
case Right :: REVOKEROLE :
$result = $this -> hasRole ( Profile_role :: OWNER );
break ;
2009-11-16 18:03:59 +00:00
case Right :: NEWNOTICE :
2009-11-16 18:46:08 +00:00
case Right :: NEWMESSAGE :
case Right :: SUBSCRIBE :
2010-12-28 19:34:02 +00:00
case Right :: CREATEGROUP :
2009-11-16 18:03:59 +00:00
$result = ! $this -> isSilenced ();
break ;
2009-11-16 18:22:22 +00:00
case Right :: PUBLICNOTICE :
2009-11-16 18:46:08 +00:00
case Right :: EMAILONREPLY :
case Right :: EMAILONSUBSCRIBE :
2009-11-16 19:28:55 +00:00
case Right :: EMAILONFAVE :
2009-11-16 18:22:22 +00:00
$result = ! $this -> isSandboxed ();
break ;
2011-02-21 15:20:42 +00:00
case Right :: WEBLOGIN :
$result = ! $this -> isSilenced ();
break ;
case Right :: API :
$result = ! $this -> isSilenced ();
break ;
2010-12-13 21:28:32 +00:00
case Right :: BACKUPACCOUNT :
$result = common_config ( 'profile' , 'backup' );
break ;
case Right :: RESTOREACCOUNT :
$result = common_config ( 'profile' , 'restore' );
break ;
case Right :: DELETEACCOUNT :
$result = common_config ( 'profile' , 'delete' );
break ;
case Right :: MOVEACCOUNT :
$result = common_config ( 'profile' , 'move' );
break ;
2009-11-16 18:03:59 +00:00
default :
$result = false ;
break ;
}
}
return $result ;
}
2009-12-11 15:49:26 +00:00
2013-10-06 20:30:29 +01:00
// FIXME: Can't put Notice typing here due to ArrayWrapper
public function hasRepeated ( $notice )
2009-12-11 15:49:26 +00:00
{
// XXX: not really a pkey, but should work
2015-12-14 20:44:59 +00:00
$notice = Notice :: pkeyGet ( array ( 'profile_id' => $this -> getID (),
'repeat_of' => $notice -> getID (),
2015-11-22 20:26:08 +00:00
'verb' => ActivityVerb :: SHARE ));
2009-12-11 15:49:26 +00:00
return ! empty ( $notice );
}
2010-02-11 02:55:14 +00:00
2010-02-12 18:54:48 +00:00
/**
* Returns an XML string fragment with limited profile information
* as an Atom < author > element .
*
* Assumes that Atom has been previously set up as the base namespace .
*
2010-06-23 00:28:06 +01:00
* @ param Profile $cur the current authenticated user
*
2010-02-12 18:54:48 +00:00
* @ return string
*/
2010-06-23 00:28:06 +01:00
function asAtomAuthor ( $cur = null )
2010-02-11 02:55:14 +00:00
{
$xs = new XMLStringer ( true );
$xs -> elementStart ( 'author' );
$xs -> element ( 'name' , null , $this -> nickname );
2010-02-17 04:13:39 +00:00
$xs -> element ( 'uri' , null , $this -> getUri ());
2010-06-23 00:28:06 +01:00
if ( $cur != null ) {
$attrs = Array ();
$attrs [ 'following' ] = $cur -> isSubscribed ( $this ) ? 'true' : 'false' ;
$attrs [ 'blocking' ] = $cur -> hasBlocked ( $this ) ? 'true' : 'false' ;
$xs -> element ( 'statusnet:profile_info' , $attrs , null );
}
2010-02-11 02:55:14 +00:00
$xs -> elementEnd ( 'author' );
return $xs -> getString ();
}
2011-02-10 07:18:14 +00:00
/**
* Extra profile info for atom entries
*
* Clients use some extra profile info in the atom stream .
* This gives it to them .
*
2014-07-28 08:25:05 +01:00
* @ param Profile $scoped The currently logged in / scoped profile
2011-02-10 07:18:14 +00:00
*
2011-02-18 03:02:57 +00:00
* @ return array representation of < statusnet : profile_info > element or null
2011-02-10 07:18:14 +00:00
*/
2014-07-28 08:25:05 +01:00
function profileInfo ( Profile $scoped = null )
2011-02-10 07:18:14 +00:00
{
2011-02-18 03:02:57 +00:00
$profileInfoAttr = array ( 'local_id' => $this -> id );
2011-02-10 07:18:14 +00:00
2014-07-28 08:25:05 +01:00
if ( $scoped instanceof Profile ) {
2011-02-10 07:18:14 +00:00
// Whether the current user is a subscribed to this profile
2014-07-28 08:25:05 +01:00
$profileInfoAttr [ 'following' ] = $scoped -> isSubscribed ( $this ) ? 'true' : 'false' ;
2011-02-10 07:18:14 +00:00
// Whether the current user is has blocked this profile
2014-07-28 08:25:05 +01:00
$profileInfoAttr [ 'blocking' ] = $scoped -> hasBlocked ( $this ) ? 'true' : 'false' ;
2011-02-10 07:18:14 +00:00
}
return array ( 'statusnet:profile_info' , $profileInfoAttr , null );
}
2010-02-12 18:54:48 +00:00
/**
* Returns an XML string fragment with profile information as an
* Activity Streams < activity : actor > element .
*
* Assumes that 'activity' namespace has been previously defined .
*
* @ return string
*/
2010-02-11 02:55:14 +00:00
function asActivityActor ()
2010-02-12 18:54:48 +00:00
{
return $this -> asActivityNoun ( 'actor' );
}
/**
* Returns an XML string fragment with profile information as an
* Activity Streams noun object with the given element type .
*
2010-02-22 09:21:34 +00:00
* Assumes that 'activity' , 'georss' , and 'poco' namespace has been
* previously defined .
2010-02-12 18:54:48 +00:00
*
* @ param string $element one of 'actor' , 'subject' , 'object' , 'target'
2010-02-22 09:21:34 +00:00
*
2010-02-12 18:54:48 +00:00
* @ return string
*/
function asActivityNoun ( $element )
2010-02-11 02:55:14 +00:00
{
2014-07-02 17:39:53 +01:00
$noun = $this -> asActivityObject ();
2010-02-23 01:10:50 +00:00
return $noun -> asString ( 'activity:' . $element );
2010-02-11 02:55:14 +00:00
}
2010-02-11 23:24:18 +00:00
2014-07-02 17:39:53 +01:00
public function asActivityObject ()
{
$object = new ActivityObject ();
if ( Event :: handle ( 'StartActivityObjectFromProfile' , array ( $this , & $object ))) {
$object -> type = $this -> getObjectType ();
$object -> id = $this -> getUri ();
$object -> title = $this -> getBestName ();
$object -> link = $this -> getUrl ();
2014-08-07 20:54:31 +01:00
$object -> summary = $this -> getDescription ();
2014-07-02 17:39:53 +01:00
try {
$avatar = Avatar :: getUploaded ( $this );
$object -> avatarLinks [] = AvatarLink :: fromAvatar ( $avatar );
} catch ( NoAvatarException $e ) {
// Could not find an original avatar to link
}
$sizes = array (
AVATAR_PROFILE_SIZE ,
AVATAR_STREAM_SIZE ,
AVATAR_MINI_SIZE
);
foreach ( $sizes as $size ) {
$alink = null ;
try {
$avatar = Avatar :: byProfile ( $this , $size );
$alink = AvatarLink :: fromAvatar ( $avatar );
} catch ( NoAvatarException $e ) {
$alink = new AvatarLink ();
$alink -> type = 'image/png' ;
$alink -> height = $size ;
$alink -> width = $size ;
$alink -> url = Avatar :: defaultImage ( $size );
}
$object -> avatarLinks [] = $alink ;
}
if ( isset ( $this -> lat ) && isset ( $this -> lon )) {
$object -> geopoint = ( float ) $this -> lat
. ' ' . ( float ) $this -> lon ;
}
$object -> poco = PoCo :: fromProfile ( $this );
if ( $this -> isLocal ()) {
$object -> extra [] = array ( 'followers' , array ( 'url' => common_local_url ( 'subscribers' , array ( 'nickname' => $this -> getNickname ()))));
}
Event :: handle ( 'EndActivityObjectFromProfile' , array ( $this , & $object ));
}
return $object ;
}
2013-09-30 16:13:03 +01:00
/**
* Returns the profile ' s canonical url , not necessarily a uri / unique id
*
* @ return string $profileurl
*/
public function getUrl ()
{
2016-01-07 11:58:14 +00:00
$url = null ;
if ( $this -> isGroup ()) {
// FIXME: Get rid of this event, it fills no real purpose, data should be in Profile->profileurl (replaces User_group->mainpage)
if ( Event :: handle ( 'StartUserGroupHomeUrl' , array ( $this -> getGroup (), & $url ))) {
2016-01-09 12:33:30 +00:00
$url = $this -> getGroup () -> isLocal ()
2016-01-07 11:58:14 +00:00
? common_local_url ( 'showgroup' , array ( 'nickname' => $this -> getNickname ()))
: $this -> profileurl ;
}
Event :: handle ( 'EndUserGroupHomeUrl' , array ( $this -> getGroup (), $url ));
2016-01-09 12:47:38 +00:00
} elseif ( $this -> isLocal ()) {
$url = common_local_url ( 'showstream' , array ( 'nickname' => $this -> getNickname ()));
2016-01-07 11:58:14 +00:00
} else {
$url = $this -> profileurl ;
}
2016-01-09 12:33:30 +00:00
if ( empty ( $url ) ||
! filter_var ( $url , FILTER_VALIDATE_URL )) {
throw new InvalidUrlException ( $url );
2013-09-30 16:13:03 +01:00
}
2016-01-09 12:33:30 +00:00
return $url ;
2013-09-30 16:13:03 +01:00
}
2014-04-28 13:08:42 +01:00
public function getNickname ()
{
return $this -> nickname ;
}
2015-04-22 19:57:05 +01:00
public function getFullname ()
{
return $this -> fullname ;
}
2015-07-17 11:09:24 +01:00
public function getHomepage ()
{
return $this -> homepage ;
}
2014-08-07 20:54:31 +01:00
public function getDescription ()
{
return $this -> bio ;
}
2010-02-17 04:13:39 +00:00
/**
* Returns the best URI for a profile . Plugins may override .
*
* @ return string $uri
*/
2013-09-30 16:13:03 +01:00
public function getUri ()
2010-02-11 23:24:18 +00:00
{
2010-02-17 04:13:39 +00:00
$uri = null ;
2010-02-17 00:22:58 +00:00
2010-02-22 03:52:27 +00:00
// give plugins a chance to set the URI
if ( Event :: handle ( 'StartGetProfileUri' , array ( $this , & $uri ))) {
2010-02-17 00:22:58 +00:00
2010-02-22 03:52:27 +00:00
// check for a local user first
2013-08-18 12:04:58 +01:00
$user = User :: getKV ( 'id' , $this -> id );
2014-04-28 13:08:42 +01:00
if ( $user instanceof User ) {
$uri = $user -> getUri ();
2015-12-16 02:49:42 +00:00
} else {
$group = User_group :: getKV ( 'profile_id' , $this -> id );
if ( $group instanceof User_group ) {
$uri = $group -> getUri ();
}
2010-02-17 00:22:58 +00:00
}
2011-07-15 20:13:57 +01:00
2010-02-22 03:52:27 +00:00
Event :: handle ( 'EndGetProfileUri' , array ( $this , & $uri ));
2010-02-17 00:22:58 +00:00
}
2010-02-17 04:13:39 +00:00
return $uri ;
2010-02-11 23:24:18 +00:00
}
2013-10-28 17:01:39 +00:00
/**
* Returns an assumed acct : URI for a profile . Plugins are required .
*
* @ return string $uri
*/
public function getAcctUri ()
{
$acct = null ;
if ( Event :: handle ( 'StartGetProfileAcctUri' , array ( $this , & $acct ))) {
Event :: handle ( 'EndGetProfileAcctUri' , array ( $this , & $acct ));
}
if ( $acct === null ) {
throw new ProfileNoAcctUriException ( $this );
}
return $acct ;
}
2010-02-19 13:16:45 +00:00
function hasBlocked ( $other )
{
2013-09-09 22:08:43 +01:00
$block = Profile_block :: exists ( $this , $other );
return ! empty ( $block );
2010-02-19 13:16:45 +00:00
}
2010-08-03 23:50:21 +01:00
function getAtomFeed ()
{
$feed = null ;
if ( Event :: handle ( 'StartProfileGetAtomFeed' , array ( $this , & $feed ))) {
2013-08-18 12:04:58 +01:00
$user = User :: getKV ( 'id' , $this -> id );
2010-08-03 23:50:21 +01:00
if ( ! empty ( $user )) {
$feed = common_local_url ( 'ApiTimelineUser' , array ( 'id' => $user -> id ,
'format' => 'atom' ));
}
Event :: handle ( 'EndProfileGetAtomFeed' , array ( $this , $feed ));
}
return $feed ;
}
2010-09-01 21:15:22 +01:00
2015-02-17 15:45:26 +00:00
public function repeatedToMe ( $offset = 0 , $limit = 20 , $since_id = null , $max_id = null )
{
// TRANS: Exception thrown when trying view "repeated to me".
throw new Exception ( _ ( 'Not implemented since inbox change.' ));
}
2014-05-26 14:05:14 +01:00
/*
* Get a Profile object by URI . Will call external plugins for help
* using the event StartGetProfileFromURI .
*
* @ param string $uri A unique identifier for a resource ( profile / group / whatever )
*/
static function fromUri ( $uri )
2010-09-01 21:15:22 +01:00
{
$profile = null ;
if ( Event :: handle ( 'StartGetProfileFromURI' , array ( $uri , & $profile ))) {
2014-05-26 14:05:14 +01:00
// Get a local user when plugin lookup (like OStatus) fails
2013-08-18 12:04:58 +01:00
$user = User :: getKV ( 'uri' , $uri );
2014-05-26 14:05:14 +01:00
if ( $user instanceof User ) {
2010-09-01 21:15:22 +01:00
$profile = $user -> getProfile ();
2016-01-09 13:36:47 +00:00
} else {
$group = User_group :: getKV ( 'uri' , $uri );
if ( $group instanceof User_group ) {
$profile = $group -> getProfile ();
}
2010-09-01 21:15:22 +01:00
}
Event :: handle ( 'EndGetProfileFromURI' , array ( $uri , $profile ));
}
2014-05-26 14:05:14 +01:00
if ( ! $profile instanceof Profile ) {
throw new UnknownUriException ( $uri );
}
2010-09-01 21:55:16 +01:00
return $profile ;
2010-09-01 21:15:22 +01:00
}
2011-03-17 02:55:14 +00:00
function canRead ( Notice $notice )
{
if ( $notice -> scope & Notice :: SITE_SCOPE ) {
$user = $this -> getUser ();
if ( empty ( $user )) {
return false ;
}
}
if ( $notice -> scope & Notice :: ADDRESSEE_SCOPE ) {
$replies = $notice -> getReplies ();
if ( ! in_array ( $this -> id , $replies )) {
$groups = $notice -> getGroups ();
$foundOne = false ;
foreach ( $groups as $group ) {
if ( $this -> isMember ( $group )) {
$foundOne = true ;
break ;
}
}
if ( ! $foundOne ) {
return false ;
}
}
}
if ( $notice -> scope & Notice :: FOLLOWER_SCOPE ) {
$author = $notice -> getProfile ();
if ( ! Subscription :: exists ( $this , $author )) {
return false ;
}
}
return true ;
}
2011-04-11 16:16:30 +01:00
static function current ()
{
$user = common_current_user ();
if ( empty ( $user )) {
$profile = null ;
} else {
$profile = $user -> getProfile ();
}
return $profile ;
}
2011-04-18 23:23:06 +01:00
2016-02-12 12:52:48 +00:00
static function ensureCurrent ()
{
$profile = self :: current ();
if ( ! $profile instanceof Profile ) {
throw new AuthorizationException ( 'A currently scoped profile is required.' );
}
return $profile ;
}
2011-04-18 23:23:06 +01:00
/**
* Magic function called at serialize () time .
*
* We use this to drop a couple process - specific references
* from DB_DataObject which can cause trouble in future
* processes .
*
* @ return array of variable names to include in serialization .
*/
function __sleep ()
{
$vars = parent :: __sleep ();
2014-06-05 23:07:32 +01:00
$skip = array ( '_user' , '_group' );
2011-04-18 23:23:06 +01:00
return array_diff ( $vars , $skip );
}
2011-09-19 00:29:23 +01:00
2013-10-06 00:56:27 +01:00
public function getProfile ()
2011-09-19 00:29:23 +01:00
{
return $this ;
}
2014-04-29 19:37:58 +01:00
2015-09-27 22:46:30 +01:00
/**
* Test whether the given profile is the same as the current class ,
* for testing identities .
*
* @ param Profile $other The other profile , usually from Action ' s $this -> scoped
*
* @ return boolean
*/
public function sameAs ( Profile $other = null )
2015-07-10 11:19:08 +01:00
{
2015-09-27 22:46:30 +01:00
if ( is_null ( $other )) {
// In case $this->scoped is null or something, i.e. not a current/legitimate profile.
return false ;
}
2015-07-10 11:19:08 +01:00
return $this -> getID () === $other -> getID ();
}
2014-04-29 19:37:58 +01:00
/**
* This will perform shortenLinks with the connected User object .
*
* Won ' t work on remote profiles or groups , so expect a
* NoSuchUserException if you don 't know it' s a local User .
*
* @ param string $text String to shorten
* @ param boolean $always Disrespect minimum length etc .
*
* @ return string link - shortened $text
*/
public function shortenLinks ( $text , $always = false )
{
return $this -> getUser () -> shortenLinks ( $text , $always );
}
2014-07-13 15:30:37 +01:00
2015-02-24 21:59:58 +00:00
public function isPrivateStream ()
{
// We only know of public remote users as of yet...
if ( ! $this -> isLocal ()) {
return false ;
}
return $this -> getUser () -> private_stream ? true : false ;
}
2015-01-21 21:59:15 +00:00
public function delPref ( $namespace , $topic ) {
return Profile_prefs :: setData ( $this , $namespace , $topic , null );
}
2014-07-13 15:30:37 +01:00
public function getPref ( $namespace , $topic , $default = null ) {
2015-01-21 22:45:49 +00:00
// If you want an exception to be thrown, call Profile_prefs::getData directly
try {
return Profile_prefs :: getData ( $this , $namespace , $topic , $default );
} catch ( NoResultException $e ) {
return null ;
}
2014-07-13 15:30:37 +01:00
}
2015-02-03 20:58:17 +00:00
// The same as getPref but will fall back to common_config value for the same namespace/topic
public function getConfigPref ( $namespace , $topic )
{
return Profile_prefs :: getConfigData ( $this , $namespace , $topic );
}
2014-07-13 15:30:37 +01:00
public function setPref ( $namespace , $topic , $data ) {
return Profile_prefs :: setData ( $this , $namespace , $topic , $data );
}
2015-07-16 23:20:46 +01:00
public function getConnectedApps ( $offset = 0 , $limit = null )
{
return $this -> getUser () -> getConnectedApps ( $offset , $limit );
}
2015-12-14 20:44:59 +00:00
}