2017-12-09 13:31:00 -05:00
< ? php
/**
2021-03-29 02:40:20 -04:00
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
2020-02-09 09:45:36 -05:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2017-12-09 13:31:00 -05:00
*/
2019-07-28 09:39:45 -04:00
2017-12-09 13:31:00 -05:00
namespace Friendica\Model ;
2018-10-17 15:30:41 -04:00
use Friendica\BaseModule ;
2018-10-29 17:20:46 -04:00
use Friendica\Core\Logger ;
2019-07-16 20:23:19 -04:00
use Friendica\Core\Protocol ;
2018-10-31 10:35:50 -04:00
use Friendica\Core\Renderer ;
2018-07-20 08:19:26 -04:00
use Friendica\Database\DBA ;
2020-01-18 16:07:07 -05:00
use Friendica\DI ;
2017-12-09 13:31:00 -05:00
/**
2020-01-19 01:05:23 -05:00
* functions for interacting with the group database table
2017-12-09 13:31:00 -05:00
*/
2019-12-15 17:28:01 -05:00
class Group
2017-12-09 13:31:00 -05:00
{
2019-07-14 21:48:35 -04:00
const FOLLOWERS = '~' ;
const MUTUALS = '&' ;
public static function getByUserId ( $uid , $includesDeleted = false )
{
$conditions = [ 'uid' => $uid ];
if ( ! $includesDeleted ) {
$conditions [ 'deleted' ] = false ;
}
2019-07-28 09:39:45 -04:00
return DBA :: selectToArray ( 'group' , [], $conditions );
2019-07-14 21:48:35 -04:00
}
2019-02-23 15:33:55 -05:00
/**
* @ param int $group_id
* @ return bool
* @ throws \Exception
*/
public static function exists ( $group_id , $uid = null )
{
$condition = [ 'id' => $group_id , 'deleted' => false ];
if ( isset ( $uid )) {
$condition = [
'uid' => $uid
];
}
return DBA :: exists ( 'group' , $condition );
}
2017-12-09 13:31:00 -05:00
/**
2020-01-19 01:05:23 -05:00
* Create a new contact group
2017-12-09 13:31:00 -05:00
*
* Note : If we found a deleted group with the same name , we restore it
*
2019-01-06 16:06:53 -05:00
* @ param int $uid
2017-12-09 13:31:00 -05:00
* @ param string $name
* @ return boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function create ( $uid , $name )
{
$return = false ;
2018-11-30 09:06:22 -05:00
if ( ! empty ( $uid ) && ! empty ( $name )) {
2017-12-09 13:31:00 -05:00
$gid = self :: getIdByName ( $uid , $name ); // check for dupes
if ( $gid !== false ) {
// This could be a problem.
// Let's assume we've just created a group which we once deleted
// all the old members are gone, but the group remains so we don't break any security
// access lists. What we're doing here is reviving the dead group, but old content which
// was restricted to this group may now be seen by the new group members.
2018-07-20 08:19:26 -04:00
$group = DBA :: selectFirst ( 'group' , [ 'deleted' ], [ 'id' => $gid ]);
2018-07-21 08:46:04 -04:00
if ( DBA :: isResult ( $group ) && $group [ 'deleted' ]) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'group' , [ 'deleted' => 0 ], [ 'id' => $gid ]);
2020-07-23 02:25:01 -04:00
notice ( DI :: l10n () -> t ( 'A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.' ));
2017-12-09 13:31:00 -05:00
}
return true ;
}
2018-07-20 08:19:26 -04:00
$return = DBA :: insert ( 'group' , [ 'uid' => $uid , 'name' => $name ]);
2017-12-12 20:52:50 -05:00
if ( $return ) {
2018-07-20 08:19:26 -04:00
$return = DBA :: lastInsertId ();
2017-12-12 20:52:50 -05:00
}
2017-12-09 13:31:00 -05:00
}
return $return ;
}
2018-04-07 09:54:26 -04:00
/**
* Update group information .
*
2019-07-28 09:39:45 -04:00
* @ param int $id Group ID
* @ param string $name Group name
2018-04-07 09:54:26 -04:00
*
* @ return bool Was the update successful ?
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-04-07 09:54:26 -04:00
*/
public static function update ( $id , $name )
{
2018-07-20 08:19:26 -04:00
return DBA :: update ( 'group' , [ 'name' => $name ], [ 'id' => $id ]);
2018-04-07 09:54:26 -04:00
}
2017-12-09 13:31:00 -05:00
/**
2020-01-19 01:05:23 -05:00
* Get a list of group ids a contact belongs to
2017-12-09 13:31:00 -05:00
*
* @ param int $cid
* @ return array
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
2017-12-16 19:21:56 -05:00
public static function getIdsByContactId ( $cid )
2017-12-09 13:31:00 -05:00
{
$return = [];
2017-12-10 15:12:23 -05:00
2019-07-27 18:06:29 -04:00
$stmt = DBA :: select ( 'group_member' , [ 'gid' ], [ 'contact-id' => $cid ]);
2018-07-20 08:19:26 -04:00
while ( $group = DBA :: fetch ( $stmt )) {
2017-12-10 15:12:23 -05:00
$return [] = $group [ 'gid' ];
2017-12-09 13:31:00 -05:00
}
2019-07-27 18:06:29 -04:00
DBA :: close ( $stmt );
2017-12-09 13:31:00 -05:00
2019-11-07 22:02:34 -05:00
// Meta-groups
$contact = Contact :: getById ( $cid , [ 'rel' ]);
if ( $contact [ 'rel' ] == Contact :: FOLLOWER || $contact [ 'rel' ] == Contact :: FRIEND ) {
$return [] = self :: FOLLOWERS ;
}
if ( $contact [ 'rel' ] == Contact :: FRIEND ) {
$return [] = self :: MUTUALS ;
}
2017-12-09 13:31:00 -05:00
return $return ;
}
/**
2020-01-19 01:05:23 -05:00
* count unread group items
2017-12-09 13:31:00 -05:00
*
* Count unread items of each groups of the local user
*
* @ return array
2019-01-06 16:06:53 -05:00
* 'id' => group id
* 'name' => group name
* 'count' => counted unseen group items
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function countUnseen ()
{
2018-07-20 08:19:26 -04:00
$stmt = DBA :: p ( " SELECT `group`.`id`, `group`.`name`,
2021-03-06 00:47:49 -05:00
( SELECT COUNT ( * ) FROM `post-user`
2017-12-09 13:31:00 -05:00
WHERE `uid` = ?
AND `unseen`
AND `contact-id` IN
( SELECT `contact-id`
FROM `group_member`
2017-12-14 22:47:58 -05:00
WHERE `group_member` . `gid` = `group` . `id` )
2017-12-09 13:31:00 -05:00
) AS `count`
FROM `group`
WHERE `group` . `uid` = ? ; " ,
local_user (),
local_user ()
);
2018-07-20 22:03:40 -04:00
return DBA :: toArray ( $stmt );
2017-12-09 13:31:00 -05:00
}
/**
2020-01-19 01:05:23 -05:00
* Get the group id for a user / name couple
2017-12-09 13:31:00 -05:00
*
* Returns false if no group has been found .
*
2019-01-06 16:06:53 -05:00
* @ param int $uid
2017-12-09 13:31:00 -05:00
* @ param string $name
* @ return int | boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function getIdByName ( $uid , $name )
{
2017-12-10 01:07:48 -05:00
if ( ! $uid || ! strlen ( $name )) {
2017-12-09 13:31:00 -05:00
return false ;
}
2018-07-20 08:19:26 -04:00
$group = DBA :: selectFirst ( 'group' , [ 'id' ], [ 'uid' => $uid , 'name' => $name ]);
2018-07-21 08:46:04 -04:00
if ( DBA :: isResult ( $group )) {
2017-12-09 13:31:00 -05:00
return $group [ 'id' ];
}
return false ;
}
/**
2020-01-19 01:05:23 -05:00
* Mark a group as deleted
2017-12-09 13:31:00 -05:00
*
2017-12-17 15:27:50 -05:00
* @ param int $gid
2017-12-09 13:31:00 -05:00
* @ return boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
2019-07-28 09:39:45 -04:00
public static function remove ( $gid )
{
if ( ! $gid ) {
2017-12-09 13:31:00 -05:00
return false ;
}
2018-07-20 08:19:26 -04:00
$group = DBA :: selectFirst ( 'group' , [ 'uid' ], [ 'id' => $gid ]);
2018-07-21 08:46:04 -04:00
if ( ! DBA :: isResult ( $group )) {
2017-12-17 15:31:37 -05:00
return false ;
}
2017-12-09 13:31:00 -05:00
// remove group from default posting lists
2018-07-20 08:19:26 -04:00
$user = DBA :: selectFirst ( 'user' , [ 'def_gid' , 'allow_gid' , 'deny_gid' ], [ 'uid' => $group [ 'uid' ]]);
2018-07-21 08:46:04 -04:00
if ( DBA :: isResult ( $user )) {
2017-12-09 13:31:00 -05:00
$change = false ;
if ( $user [ 'def_gid' ] == $gid ) {
$user [ 'def_gid' ] = 0 ;
$change = true ;
}
if ( strpos ( $user [ 'allow_gid' ], '<' . $gid . '>' ) !== false ) {
$user [ 'allow_gid' ] = str_replace ( '<' . $gid . '>' , '' , $user [ 'allow_gid' ]);
$change = true ;
}
if ( strpos ( $user [ 'deny_gid' ], '<' . $gid . '>' ) !== false ) {
$user [ 'deny_gid' ] = str_replace ( '<' . $gid . '>' , '' , $user [ 'deny_gid' ]);
$change = true ;
}
if ( $change ) {
2018-07-20 08:19:26 -04:00
DBA :: update ( 'user' , $user , [ 'uid' => $group [ 'uid' ]]);
2017-12-09 13:31:00 -05:00
}
}
// remove all members
2018-07-20 08:19:26 -04:00
DBA :: delete ( 'group_member' , [ 'gid' => $gid ]);
2017-12-09 13:31:00 -05:00
// remove group
2018-07-20 08:19:26 -04:00
$return = DBA :: update ( 'group' , [ 'deleted' => 1 ], [ 'id' => $gid ]);
2017-12-09 13:31:00 -05:00
return $return ;
}
/**
2020-01-19 01:05:23 -05:00
* Adds a contact to a group
2017-12-09 13:31:00 -05:00
*
* @ param int $gid
* @ param int $cid
* @ return boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function addMember ( $gid , $cid )
{
2017-12-10 01:06:12 -05:00
if ( ! $gid || ! $cid ) {
2017-12-09 13:31:00 -05:00
return false ;
}
2018-07-20 08:19:26 -04:00
$row_exists = DBA :: exists ( 'group_member' , [ 'gid' => $gid , 'contact-id' => $cid ]);
2017-12-09 13:31:00 -05:00
if ( $row_exists ) {
// Row already existing, nothing to do
$return = true ;
} else {
2018-07-20 08:19:26 -04:00
$return = DBA :: insert ( 'group_member' , [ 'gid' => $gid , 'contact-id' => $cid ]);
2017-12-09 13:31:00 -05:00
}
return $return ;
}
/**
2020-01-19 01:05:23 -05:00
* Removes a contact from a group
2017-12-09 13:31:00 -05:00
*
* @ param int $gid
* @ param int $cid
* @ return boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function removeMember ( $gid , $cid )
{
2017-12-10 01:06:12 -05:00
if ( ! $gid || ! $cid ) {
2017-12-09 13:31:00 -05:00
return false ;
}
2018-07-20 08:19:26 -04:00
$return = DBA :: delete ( 'group_member' , [ 'gid' => $gid , 'contact-id' => $cid ]);
2017-12-09 13:31:00 -05:00
return $return ;
}
/**
2020-01-19 01:05:23 -05:00
* Returns the combined list of contact ids from a group id list
2017-12-09 13:31:00 -05:00
*
2019-07-14 21:48:35 -04:00
* @ param int $uid
2019-01-06 16:06:53 -05:00
* @ param array $group_ids
2017-12-09 13:31:00 -05:00
* @ param boolean $check_dead
* @ return array
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
2019-07-14 21:48:35 -04:00
public static function expand ( $uid , array $group_ids , $check_dead = false )
2017-12-09 13:31:00 -05:00
{
2017-12-10 01:06:12 -05:00
if ( ! is_array ( $group_ids ) || ! count ( $group_ids )) {
2017-12-09 13:31:00 -05:00
return [];
}
2017-12-10 01:06:12 -05:00
$return = [];
2019-12-02 18:04:11 -05:00
$pubmail = false ;
$networks = Protocol :: SUPPORT_PRIVATE ;
$mailacct = DBA :: selectFirst ( 'mailacct' , [ 'pubmail' ], [ '`uid` = ? AND `server` != ""' , $uid ]);
if ( DBA :: isResult ( $mailacct )) {
$pubmail = $mailacct [ 'pubmail' ];
}
if ( ! $pubmail ) {
$networks = array_diff ( $networks , [ Protocol :: MAIL ]);
}
2019-07-14 21:48:35 -04:00
$key = array_search ( self :: FOLLOWERS , $group_ids );
if ( $key !== false ) {
2019-07-27 18:06:29 -04:00
$followers = Contact :: selectToArray ([ 'id' ], [
2019-07-16 20:23:19 -04:00
'uid' => $uid ,
'rel' => [ Contact :: FOLLOWER , Contact :: FRIEND ],
2019-12-02 18:04:11 -05:00
'network' => $networks ,
'contact-type' => [ Contact :: TYPE_UNKNOWN , Contact :: TYPE_PERSON ],
'archive' => false ,
'pending' => false ,
'blocked' => false ,
2019-07-16 20:23:19 -04:00
]);
2019-07-14 21:48:35 -04:00
2019-07-27 18:06:29 -04:00
foreach ( $followers as $follower ) {
2019-07-14 21:48:35 -04:00
$return [] = $follower [ 'id' ];
}
unset ( $group_ids [ $key ]);
}
$key = array_search ( self :: MUTUALS , $group_ids );
if ( $key !== false ) {
2019-07-27 18:06:29 -04:00
$mutuals = Contact :: selectToArray ([ 'id' ], [
2019-07-16 20:23:19 -04:00
'uid' => $uid ,
'rel' => [ Contact :: FRIEND ],
2019-12-02 18:04:11 -05:00
'network' => $networks ,
'contact-type' => [ Contact :: TYPE_UNKNOWN , Contact :: TYPE_PERSON ],
'archive' => false ,
'pending' => false ,
'blocked' => false ,
2019-07-16 20:23:19 -04:00
]);
2019-07-14 21:48:35 -04:00
2019-07-27 18:06:29 -04:00
foreach ( $mutuals as $mutual ) {
2019-07-14 21:48:35 -04:00
$return [] = $mutual [ 'id' ];
}
unset ( $group_ids [ $key ]);
}
$stmt = DBA :: select ( 'group_member' , [ 'contact-id' ], [ 'gid' => $group_ids ]);
2019-07-28 09:39:45 -04:00
while ( $group_member = DBA :: fetch ( $stmt )) {
2017-12-10 01:06:12 -05:00
$return [] = $group_member [ 'contact-id' ];
2017-12-09 13:31:00 -05:00
}
2019-07-27 18:06:29 -04:00
DBA :: close ( $stmt );
2017-12-09 13:31:00 -05:00
2018-08-23 09:51:58 -04:00
if ( $check_dead ) {
2020-01-05 17:07:33 -05:00
$return = Contact :: pruneUnavailable ( $return );
2017-12-09 13:31:00 -05:00
}
2018-08-23 09:51:58 -04:00
2017-12-09 13:31:00 -05:00
return $return ;
}
/**
2020-01-19 01:05:23 -05:00
* Returns a templated group selection list
2017-12-09 13:31:00 -05:00
*
2019-01-06 16:06:53 -05:00
* @ param int $uid
* @ param int $gid An optional pre - selected group
2017-12-09 13:31:00 -05:00
* @ param string $label An optional label of the list
* @ return string
2019-07-14 21:48:35 -04:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
public static function displayGroupSelection ( $uid , $gid = 0 , $label = '' )
{
$display_groups = [
[
'name' => '' ,
'id' => '0' ,
'selected' => ''
]
];
2019-07-27 18:06:29 -04:00
$stmt = DBA :: select ( 'group' , [], [ 'deleted' => 0 , 'uid' => $uid ], [ 'order' => [ 'name' ]]);
2018-07-20 08:19:26 -04:00
while ( $group = DBA :: fetch ( $stmt )) {
2017-12-09 13:31:00 -05:00
$display_groups [] = [
'name' => $group [ 'name' ],
'id' => $group [ 'id' ],
'selected' => $gid == $group [ 'id' ] ? 'true' : ''
];
}
2019-07-27 18:06:29 -04:00
DBA :: close ( $stmt );
2019-07-28 09:39:45 -04:00
Logger :: info ( 'Got groups' , $display_groups );
2017-12-09 13:31:00 -05:00
if ( $label == '' ) {
2020-01-18 14:52:34 -05:00
$label = DI :: l10n () -> t ( 'Default privacy group for new contacts' );
2017-12-09 13:31:00 -05:00
}
2018-10-31 10:44:06 -04:00
$o = Renderer :: replaceMacros ( Renderer :: getMarkupTemplate ( 'group_selection.tpl' ), [
2017-12-09 13:31:00 -05:00
'$label' => $label ,
'$groups' => $display_groups
2018-01-15 08:05:12 -05:00
]);
2017-12-09 13:31:00 -05:00
return $o ;
}
/**
2020-01-19 01:05:23 -05:00
* Create group sidebar widget
2017-12-09 13:31:00 -05:00
*
* @ param string $every
* @ param string $each
* @ param string $editmode
2019-01-06 16:06:53 -05:00
* 'standard' => include link 'Edit groups'
* 'extended' => include link 'Create new group'
* 'full' => include link 'Create new group' and provide for each group a link to edit this group
* @ param string $group_id
* @ param int $cid
2017-12-09 13:31:00 -05:00
* @ return string
2019-07-14 21:48:35 -04:00
* @ throws \Exception
2017-12-09 13:31:00 -05:00
*/
2018-10-14 18:02:54 -04:00
public static function sidebarWidget ( $every = 'contact' , $each = 'group' , $editmode = 'standard' , $group_id = '' , $cid = 0 )
2017-12-09 13:31:00 -05:00
{
if ( ! local_user ()) {
return '' ;
}
$display_groups = [
[
2020-01-18 14:52:34 -05:00
'text' => DI :: l10n () -> t ( 'Everybody' ),
2017-12-09 13:31:00 -05:00
'id' => 0 ,
2018-07-09 18:36:50 -04:00
'selected' => (( $group_id === 'everyone' ) ? 'group-selected' : '' ),
2017-12-09 13:31:00 -05:00
'href' => $every ,
]
];
2018-01-15 08:05:12 -05:00
$member_of = [];
2017-12-09 13:31:00 -05:00
if ( $cid ) {
2017-12-16 11:50:58 -05:00
$member_of = self :: getIdsByContactId ( $cid );
2017-12-09 13:31:00 -05:00
}
2019-07-27 18:06:29 -04:00
$stmt = DBA :: select ( 'group' , [], [ 'deleted' => 0 , 'uid' => local_user ()], [ 'order' => [ 'name' ]]);
2018-07-20 08:19:26 -04:00
while ( $group = DBA :: fetch ( $stmt )) {
2017-12-10 01:06:12 -05:00
$selected = (( $group_id == $group [ 'id' ]) ? ' group-selected' : '' );
2017-12-09 13:31:00 -05:00
2017-12-10 01:06:12 -05:00
if ( $editmode == 'full' ) {
$groupedit = [
'href' => 'group/' . $group [ 'id' ],
2020-01-18 14:52:34 -05:00
'title' => DI :: l10n () -> t ( 'edit' ),
2017-12-09 13:31:00 -05:00
];
2017-12-10 01:06:12 -05:00
} else {
$groupedit = null ;
2017-12-09 13:31:00 -05:00
}
2017-12-10 01:06:12 -05:00
2020-10-07 02:19:09 -04:00
if ( $each == 'group' ) {
$count = DBA :: count ( 'group_member' , [ 'gid' => $group [ 'id' ]]);
$group_name = sprintf ( '%s (%d)' , $group [ 'name' ], $count );
} else {
$group_name = $group [ 'name' ];
}
2017-12-10 01:06:12 -05:00
$display_groups [] = [
'id' => $group [ 'id' ],
'cid' => $cid ,
2020-10-07 02:19:09 -04:00
'text' => $group_name ,
2017-12-10 01:06:12 -05:00
'href' => $each . '/' . $group [ 'id' ],
'edit' => $groupedit ,
'selected' => $selected ,
'ismember' => in_array ( $group [ 'id' ], $member_of ),
];
2017-12-09 13:31:00 -05:00
}
2019-07-27 18:06:29 -04:00
DBA :: close ( $stmt );
2017-12-09 13:31:00 -05:00
2018-12-14 01:33:57 -05:00
// Don't show the groups on the network page when there is only one
if (( count ( $display_groups ) <= 2 ) && ( $each == 'network' )) {
2018-11-24 07:10:30 -05:00
return '' ;
}
2018-10-31 10:44:06 -04:00
$tpl = Renderer :: getMarkupTemplate ( 'group_side.tpl' );
2018-10-31 10:35:50 -04:00
$o = Renderer :: replaceMacros ( $tpl , [
2020-01-18 14:52:34 -05:00
'$add' => DI :: l10n () -> t ( 'add' ),
'$title' => DI :: l10n () -> t ( 'Groups' ),
2017-12-09 13:31:00 -05:00
'$groups' => $display_groups ,
'newgroup' => $editmode == 'extended' || $editmode == 'full' ? 1 : '' ,
'grouppage' => 'group/' ,
2020-01-18 14:52:34 -05:00
'$edittext' => DI :: l10n () -> t ( 'Edit group' ),
'$ungrouped' => $every === 'contact' ? DI :: l10n () -> t ( 'Contacts not in any group' ) : '' ,
2018-07-09 18:36:50 -04:00
'$ungrouped_selected' => (( $group_id === 'none' ) ? 'group-selected' : '' ),
2020-01-18 14:52:34 -05:00
'$createtext' => DI :: l10n () -> t ( 'Create a new group' ),
'$creategroup' => DI :: l10n () -> t ( 'Group Name: ' ),
'$editgroupstext' => DI :: l10n () -> t ( 'Edit groups' ),
2018-10-17 15:30:41 -04:00
'$form_security_token' => BaseModule :: getFormSecurityToken ( 'group_edit' ),
2017-12-09 13:31:00 -05:00
]);
return $o ;
}
}