2018-03-24 19:38:05 +01:00
< ? php
/**
* @ file src / Core / Acl . php
*/
namespace Friendica\Core ;
use Friendica\BaseObject ;
use Friendica\Content\Feature ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2018-03-24 19:38:05 +01:00
use Friendica\Model\Contact ;
use Friendica\Model\GContact ;
use Friendica\Util\Network ;
/**
* Handle ACL management and display
*
2018-09-16 01:28:38 +02:00
* @ author Hypolite Petovan < hypolite @ mrpetovan . com >
2018-03-24 19:38:05 +01:00
*/
class ACL extends BaseObject
{
/**
* Returns a select input tag with all the contact of the local user
*
2019-01-06 22:06:53 +01:00
* @ param string $selname Name attribute of the select input tag
* @ param string $selclass Class attribute of the select input tag
* @ param array $options Available options :
* - size : length of the select box
* - mutual_friends : Only used for the hook
* - single : Only used for the hook
* - exclude : Only used for the hook
* @ param array $preselected Contact ID that should be already selected
2018-03-24 19:38:05 +01:00
* @ return string
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-03-24 19:38:05 +01:00
*/
public static function getSuggestContactSelectHTML ( $selname , $selclass , array $options = [], array $preselected = [])
{
$a = self :: getApp ();
$networks = null ;
$size = defaults ( $options , 'size' , 4 );
$mutual = ! empty ( $options [ 'mutual_friends' ]);
$single = ! empty ( $options [ 'single' ]) && empty ( $options [ 'multiple' ]);
$exclude = defaults ( $options , 'exclude' , false );
switch ( defaults ( $options , 'networks' , Protocol :: PHANTOM )) {
case 'DFRN_ONLY' :
2018-08-11 22:40:44 +02:00
$networks = [ Protocol :: DFRN ];
2018-03-24 19:38:05 +01:00
break ;
2018-08-11 22:40:44 +02:00
2018-03-24 19:38:05 +01:00
case 'PRIVATE' :
2018-10-06 05:17:44 +02:00
$networks = [ Protocol :: ACTIVITYPUB , Protocol :: DFRN , Protocol :: MAIL , Protocol :: DIASPORA ];
2018-03-24 19:38:05 +01:00
break ;
2018-08-11 22:40:44 +02:00
2018-03-24 19:38:05 +01:00
case 'TWO_WAY' :
if ( ! empty ( $a -> user [ 'prvnets' ])) {
2018-10-06 05:17:44 +02:00
$networks = [ Protocol :: ACTIVITYPUB , Protocol :: DFRN , Protocol :: MAIL , Protocol :: DIASPORA ];
2018-03-24 19:38:05 +01:00
} else {
2018-10-06 05:17:44 +02:00
$networks = [ Protocol :: ACTIVITYPUB , Protocol :: DFRN , Protocol :: MAIL , Protocol :: DIASPORA , Protocol :: OSTATUS ];
2018-03-24 19:38:05 +01:00
}
break ;
2018-08-11 22:40:44 +02:00
2018-03-24 19:38:05 +01:00
default : /// @TODO Maybe log this call?
break ;
}
$x = [ 'options' => $options , 'size' => $size , 'single' => $single , 'mutual' => $mutual , 'exclude' => $exclude , 'networks' => $networks ];
2018-12-26 07:06:24 +01:00
Hook :: callAll ( 'contact_select_options' , $x );
2018-03-24 19:38:05 +01:00
$o = '' ;
$sql_extra = '' ;
if ( ! empty ( $x [ 'mutual' ])) {
2018-07-25 04:53:46 +02:00
$sql_extra .= sprintf ( " AND `rel` = %d " , intval ( Contact :: FRIEND ));
2018-03-24 19:38:05 +01:00
}
if ( ! empty ( $x [ 'exclude' ])) {
$sql_extra .= sprintf ( " AND `id` != %d " , intval ( $x [ 'exclude' ]));
}
if ( ! empty ( $x [ 'networks' ])) {
/// @TODO rewrite to foreach()
array_walk ( $x [ 'networks' ], function ( & $value ) {
2018-07-21 15:10:13 +02:00
$value = " ' " . DBA :: escape ( $value ) . " ' " ;
2018-03-24 19:38:05 +01:00
});
$str_nets = implode ( ',' , $x [ 'networks' ]);
$sql_extra .= " AND `network` IN ( $str_nets ) " ;
}
$tabindex = ( ! empty ( $options [ 'tabindex' ]) ? 'tabindex="' . $options [ " tabindex " ] . '"' : '' );
if ( ! empty ( $x [ 'single' ])) {
$o .= " <select name= \" $selname\ " id = \ " $selclass\ " class = \ " $selclass\ " size = \ " " . $x [ 'size' ] . " \" $tabindex > \r \n " ;
} else {
$o .= " <select name= \" { $selname } [] \" id= \" $selclass\ " class = \ " $selclass\ " multiple = \ " multiple \" size= \" " . $x [ 'size' ] . " $\ " $tabindex > \r\n " ;
}
2018-07-20 14:19:26 +02:00
$stmt = DBA :: p ( " SELECT `id`, `name`, `url`, `network` FROM `contact`
2019-01-09 14:23:11 +01:00
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
2018-03-24 19:38:05 +01:00
$sql_extra
ORDER BY `name` ASC " , intval(local_user())
);
2018-07-21 04:03:40 +02:00
$contacts = DBA :: toArray ( $stmt );
2018-03-24 19:38:05 +01:00
$arr = [ 'contact' => $contacts , 'entry' => $o ];
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
2018-12-26 07:06:24 +01:00
Hook :: callAll ( $a -> module . '_pre_' . $selname , $arr );
2018-03-24 19:38:05 +01:00
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $contacts )) {
2018-03-24 19:38:05 +01:00
foreach ( $contacts as $contact ) {
if ( in_array ( $contact [ 'id' ], $preselected )) {
$selected = ' selected="selected" ' ;
} else {
$selected = '' ;
}
$trimmed = mb_substr ( $contact [ 'name' ], 0 , 20 );
$o .= " <option value= \" { $contact [ 'id' ] } \" $selected title= \" { $contact [ 'name' ] } | { $contact [ 'url' ] } \" > $trimmed </option> \r \n " ;
}
}
$o .= '</select>' . PHP_EOL ;
2018-12-26 07:06:24 +01:00
Hook :: callAll ( $a -> module . '_post_' . $selname , $o );
2018-03-24 19:38:05 +01:00
return $o ;
}
/**
* Returns a select input tag with all the contact of the local user
*
* @ param string $selname Name attribute of the select input tag
* @ param string $selclass Class attribute of the select input tag
* @ param array $preselected Contact IDs that should be already selected
* @ param int $size Length of the select box
* @ param int $tabindex Select input tag tabindex attribute
* @ return string
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-03-24 19:38:05 +01:00
*/
public static function getMessageContactSelectHTML ( $selname , $selclass , array $preselected = [], $size = 4 , $tabindex = null )
{
$a = self :: getApp ();
$o = '' ;
// When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
// to one recipient. By default our selector allows multiple selects amongst all contacts.
2018-07-25 04:53:46 +02:00
$sql_extra = sprintf ( " AND `rel` = %d " , intval ( Contact :: FRIEND ));
2018-08-11 22:40:44 +02:00
$sql_extra .= sprintf ( " AND `network` IN ('%s' , '%s') " , Protocol :: DFRN , Protocol :: DIASPORA );
2018-03-24 19:38:05 +01:00
$tabindex_attr = ! empty ( $tabindex ) ? ' tabindex="' . intval ( $tabindex ) . '"' : '' ;
$hidepreselected = '' ;
if ( $preselected ) {
$sql_extra .= " AND `id` IN ( " . implode ( " , " , $preselected ) . " ) " ;
$hidepreselected = ' style="display: none;"' ;
}
$o .= " <select name= \" $selname\ " id = \ " $selclass\ " class = \ " $selclass\ " size = \ " $size\ " $tabindex_attr $hidepreselected > \r\n " ;
2018-07-20 14:19:26 +02:00
$stmt = DBA :: p ( " SELECT `id`, `name`, `url`, `network` FROM `contact`
2019-01-09 14:23:11 +01:00
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND NOT `deleted` AND `notify` != ''
2018-03-24 19:38:05 +01:00
$sql_extra
ORDER BY `name` ASC " , intval(local_user())
);
2018-07-21 04:03:40 +02:00
$contacts = DBA :: toArray ( $stmt );
2018-03-24 19:38:05 +01:00
$arr = [ 'contact' => $contacts , 'entry' => $o ];
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
2018-12-26 07:06:24 +01:00
Hook :: callAll ( $a -> module . '_pre_' . $selname , $arr );
2018-03-24 19:38:05 +01:00
$receiverlist = [];
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $contacts )) {
2018-03-24 19:38:05 +01:00
foreach ( $contacts as $contact ) {
if ( in_array ( $contact [ 'id' ], $preselected )) {
$selected = ' selected="selected"' ;
} else {
$selected = '' ;
}
$trimmed = Protocol :: formatMention ( $contact [ 'url' ], $contact [ 'name' ]);
$receiverlist [] = $trimmed ;
$o .= " <option value= \" { $contact [ 'id' ] } \" $selected title= \" { $contact [ 'name' ] } | { $contact [ 'url' ] } \" > $trimmed </option> \r \n " ;
}
}
$o .= '</select>' . PHP_EOL ;
if ( $preselected ) {
$o .= implode ( ', ' , $receiverlist );
}
2018-12-26 07:06:24 +01:00
Hook :: callAll ( $a -> module . '_post_' . $selname , $o );
2018-03-24 19:38:05 +01:00
return $o ;
}
2018-03-24 20:40:35 +01:00
private static function fixACL ( & $item )
{
2018-03-24 19:38:05 +01:00
$item = intval ( str_replace ([ '<' , '>' ], [ '' , '' ], $item ));
}
/**
* Return the default permission of the provided user array
*
* @ param array $user
* @ return array Hash of contact id lists
2019-01-06 22:06:53 +01:00
* @ throws \Exception
2018-03-24 19:38:05 +01:00
*/
public static function getDefaultUserPermissions ( array $user = null )
{
$matches = [];
$acl_regex = '/<([0-9]+)>/i' ;
preg_match_all ( $acl_regex , defaults ( $user , 'allow_cid' , '' ), $matches );
$allow_cid = $matches [ 1 ];
preg_match_all ( $acl_regex , defaults ( $user , 'allow_gid' , '' ), $matches );
$allow_gid = $matches [ 1 ];
preg_match_all ( $acl_regex , defaults ( $user , 'deny_cid' , '' ), $matches );
$deny_cid = $matches [ 1 ];
preg_match_all ( $acl_regex , defaults ( $user , 'deny_gid' , '' ), $matches );
$deny_gid = $matches [ 1 ];
// Reformats the ACL data so that it is accepted by the JS frontend
2018-03-24 20:40:35 +01:00
array_walk ( $allow_cid , 'self::fixACL' );
array_walk ( $allow_gid , 'self::fixACL' );
array_walk ( $deny_cid , 'self::fixACL' );
array_walk ( $deny_gid , 'self::fixACL' );
2018-03-24 19:38:05 +01:00
Contact :: pruneUnavailable ( $allow_cid );
return [
'allow_cid' => $allow_cid ,
'allow_gid' => $allow_gid ,
'deny_cid' => $deny_cid ,
'deny_gid' => $deny_gid ,
];
}
/**
* Return the full jot ACL selector HTML
*
2018-08-15 00:43:27 +02:00
* @ param array $user User array
2018-03-24 19:38:05 +01:00
* @ param bool $show_jotnets
2019-01-06 22:06:53 +01:00
* @ param array $default_permissions Static defaults permission array : [ 'allow_cid' => '' , 'allow_gid' => '' , 'deny_cid' => '' , 'deny_gid' => '' ]
2018-03-24 19:38:05 +01:00
* @ return string
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-03-24 19:38:05 +01:00
*/
2019-05-29 03:08:48 +02:00
public static function getFullSelectorHTML ( array $user = null , $show_jotnets = false , array $default_permissions = [])
2018-03-24 19:38:05 +01:00
{
2018-08-15 00:43:27 +02:00
// Defaults user permissions
if ( empty ( $default_permissions )) {
$default_permissions = self :: getDefaultUserPermissions ( $user );
2018-07-20 20:07:54 +02:00
}
2019-03-25 03:40:50 +01:00
$jotnets_fields = [];
2018-03-24 19:38:05 +01:00
if ( $show_jotnets ) {
$mail_enabled = false ;
$pubmail_enabled = false ;
2019-03-25 03:40:50 +01:00
if ( function_exists ( 'imap_open' ) && ! Config :: get ( 'system' , 'imap_disabled' )) {
2018-07-20 14:19:26 +02:00
$mailacct = DBA :: selectFirst ( 'mailacct' , [ 'pubmail' ], [ '`uid` = ? AND `server` != ""' , local_user ()]);
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $mailacct )) {
2018-03-24 19:38:05 +01:00
$mail_enabled = true ;
$pubmail_enabled = ! empty ( $mailacct [ 'pubmail' ]);
}
}
2018-08-15 00:43:27 +02:00
if ( empty ( $default_permissions [ 'hidewall' ])) {
2018-03-24 19:38:05 +01:00
if ( $mail_enabled ) {
2019-03-25 03:40:50 +01:00
$jotnets_fields [] = [
'type' => 'checkbox' ,
'field' => [
'pubmail_enable' ,
L10n :: t ( 'Post to Email' ),
$pubmail_enabled
]
];
2018-03-24 19:38:05 +01:00
}
2019-03-25 03:40:50 +01:00
Hook :: callAll ( 'jot_networks' , $jotnets_fields );
2018-03-24 19:38:05 +01:00
}
}
2019-03-25 03:40:50 +01:00
2018-10-31 15:44:06 +01:00
$tpl = Renderer :: getMarkupTemplate ( 'acl_selector.tpl' );
2018-10-31 15:35:50 +01:00
$o = Renderer :: replaceMacros ( $tpl , [
2018-03-24 19:38:05 +01:00
'$showall' => L10n :: t ( 'Visible to everybody' ),
'$show' => L10n :: t ( 'show' ),
'$hide' => L10n :: t ( 'don\'t show' ),
2019-02-11 10:00:42 +01:00
'$allowcid' => json_encode ( defaults ( $default_permissions , 'allow_cid' , [])), // we need arrays for Javascript since we call .remove() and .push() on this values
'$allowgid' => json_encode ( defaults ( $default_permissions , 'allow_gid' , [])),
'$denycid' => json_encode ( defaults ( $default_permissions , 'deny_cid' , [])),
'$denygid' => json_encode ( defaults ( $default_permissions , 'deny_gid' , [])),
2018-03-24 19:38:05 +01:00
'$networks' => $show_jotnets ,
'$emailcc' => L10n :: t ( 'CC: email addresses' ),
'$emtitle' => L10n :: t ( 'Example: bob@example.com, mary@example.com' ),
2019-03-25 03:40:50 +01:00
'$jotnets_enabled' => empty ( $default_permissions [ 'hidewall' ]),
'$jotnets_summary' => L10n :: t ( 'Connectors' ),
'$jotnets_fields' => $jotnets_fields ,
'$jotnets_disabled_label' => L10n :: t ( 'Connectors disabled, since "%s" is enabled.' , L10n :: t ( 'Hide your profile details from unknown viewers?' )),
2018-03-24 19:38:05 +01:00
'$aclModalTitle' => L10n :: t ( 'Permissions' ),
'$aclModalDismiss' => L10n :: t ( 'Close' ),
'$features' => [
2019-05-29 03:08:48 +02:00
'aclautomention' => ! empty ( $user [ 'uid' ]) && Feature :: isEnabled ( $user [ 'uid' ], 'aclautomention' ) ? 'true' : 'false'
2018-03-24 19:38:05 +01:00
],
]);
return $o ;
}
/**
* Searching for global contacts for autocompletion
*
* @ brief Searching for global contacts for autocompletion
* @ param string $search Name or part of a name or nick
* @ param string $mode Search mode ( e . g . " community " )
* @ return array with the search results
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-03-24 19:38:05 +01:00
*/
public static function contactAutocomplete ( $search , $mode )
{
2018-08-18 08:20:50 +02:00
if ( Config :: get ( 'system' , 'block_public' ) && ! local_user () && ! remote_user ()) {
2018-03-24 19:38:05 +01:00
return [];
}
// don't search if search term has less than 2 characters
if ( ! $search || mb_strlen ( $search ) < 2 ) {
return [];
}
if ( substr ( $search , 0 , 1 ) === '@' ) {
$search = substr ( $search , 1 );
}
// check if searching in the local global contact table is enabled
if ( Config :: get ( 'system' , 'poco_local_search' )) {
$return = GContact :: searchByName ( $search , $mode );
} else {
2018-10-24 08:15:24 +02:00
$p = defaults ( $_GET , 'page' , 1 ) != 1 ? '&p=' . defaults ( $_GET , 'page' , 1 ) : '' ;
2018-03-24 19:38:05 +01:00
2018-10-10 21:08:43 +02:00
$curlResult = Network :: curl ( get_server () . '/lsearch?f=' . $p . '&search=' . urlencode ( $search ));
if ( $curlResult -> isSuccess ()) {
$lsearch = json_decode ( $curlResult -> getBody (), true );
2018-03-24 19:38:05 +01:00
if ( ! empty ( $lsearch [ 'results' ])) {
$return = $lsearch [ 'results' ];
}
}
}
return defaults ( $return , []);
}
}