2020-02-16 04:38:56 -05:00
< ? php
/**
2021-03-29 02:40:20 -04:00
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
2020-02-16 04:38:56 -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 />.
*
* See update_profile . php for documentation
*/
namespace Friendica\Module\Conversation ;
use Friendica\BaseModule ;
use Friendica\Content\BoundariesPager ;
use Friendica\Content\Feature ;
use Friendica\Content\Nav ;
2020-09-27 01:25:32 -04:00
use Friendica\Content\Text\HTML ;
2020-10-05 12:48:28 -04:00
use Friendica\Content\Widget ;
2020-02-16 04:38:56 -05:00
use Friendica\Content\Widget\TrendingTags ;
use Friendica\Core\Renderer ;
use Friendica\Core\Session ;
use Friendica\Database\DBA ;
use Friendica\DI ;
use Friendica\Model\Item ;
2021-01-15 23:16:09 -05:00
use Friendica\Model\Post ;
2020-02-16 04:38:56 -05:00
use Friendica\Model\User ;
use Friendica\Network\HTTPException ;
class Community extends BaseModule
{
protected static $page_style ;
protected static $content ;
2020-10-13 22:30:40 -04:00
protected static $accountTypeString ;
protected static $accountType ;
2020-02-16 04:38:56 -05:00
protected static $itemsPerPage ;
2020-10-12 23:45:02 -04:00
protected static $min_id ;
2020-02-16 04:38:56 -05:00
protected static $max_id ;
2020-10-06 01:18:54 -04:00
protected static $item_id ;
2020-02-16 04:38:56 -05:00
2021-11-20 09:38:03 -05:00
protected function content ( array $request = []) : string
2020-02-16 04:38:56 -05:00
{
2021-11-14 17:19:25 -05:00
$this -> parseRequest ();
2020-02-16 04:38:56 -05:00
2020-09-27 01:31:39 -04:00
if ( DI :: pConfig () -> get ( local_user (), 'system' , 'infinite_scroll' )) {
2020-09-27 01:25:32 -04:00
$tpl = Renderer :: getMarkupTemplate ( 'infinite_scroll_head.tpl' );
$o = Renderer :: replaceMacros ( $tpl , [ '$reload_uri' => DI :: args () -> getQueryString ()]);
} else {
$o = '' ;
}
2020-09-27 01:46:07 -04:00
if ( empty ( $_GET [ 'mode' ]) || ( $_GET [ 'mode' ] != 'raw' )) {
$tabs = [];
if (( Session :: isAuthenticated () || in_array ( self :: $page_style , [ CP_USERS_AND_GLOBAL , CP_USERS_ON_SERVER ])) && empty ( DI :: config () -> get ( 'system' , 'singleuser' ))) {
$tabs [] = [
'label' => DI :: l10n () -> t ( 'Local Community' ),
'url' => 'community/local' ,
'sel' => self :: $content == 'local' ? 'active' : '' ,
'title' => DI :: l10n () -> t ( 'Posts from local users on this server' ),
'id' => 'community-local-tab' ,
'accesskey' => 'l'
];
}
2020-09-27 01:25:32 -04:00
2020-09-27 01:46:07 -04:00
if ( Session :: isAuthenticated () || in_array ( self :: $page_style , [ CP_USERS_AND_GLOBAL , CP_GLOBAL_COMMUNITY ])) {
$tabs [] = [
'label' => DI :: l10n () -> t ( 'Global Community' ),
'url' => 'community/global' ,
'sel' => self :: $content == 'global' ? 'active' : '' ,
'title' => DI :: l10n () -> t ( 'Posts from users of the whole federated network' ),
'id' => 'community-global-tab' ,
'accesskey' => 'g'
];
}
2020-09-27 01:25:32 -04:00
$tab_tpl = Renderer :: getMarkupTemplate ( 'common_tabs.tpl' );
$o .= Renderer :: replaceMacros ( $tab_tpl , [ '$tabs' => $tabs ]);
2020-02-16 04:38:56 -05:00
2020-09-27 01:46:07 -04:00
Nav :: setSelected ( 'community' );
2020-10-13 22:30:40 -04:00
DI :: page ()[ 'aside' ] .= Widget :: accounttypes ( 'community/' . self :: $content , self :: $accountTypeString );
2020-09-27 01:46:07 -04:00
if ( local_user () && DI :: config () -> get ( 'system' , 'community_no_sharer' )) {
2020-09-28 07:27:48 -04:00
$path = self :: $content ;
2021-11-14 17:19:25 -05:00
if ( ! empty ( $this -> parameters [ 'accounttype' ])) {
$path .= '/' . $this -> parameters [ 'accounttype' ];
2020-09-28 07:27:48 -04:00
}
2020-09-27 01:46:07 -04:00
$query_parameters = [];
2020-10-12 23:45:02 -04:00
if ( ! empty ( $_GET [ 'min_id' ])) {
$query_parameters [ 'min_id' ] = $_GET [ 'min_id' ];
2020-09-27 01:46:07 -04:00
}
if ( ! empty ( $_GET [ 'max_id' ])) {
$query_parameters [ 'max_id' ] = $_GET [ 'max_id' ];
}
if ( ! empty ( $_GET [ 'last_commented' ])) {
$query_parameters [ 'max_id' ] = $_GET [ 'last_commented' ];
}
$path_all = $path . ( ! empty ( $query_parameters ) ? '?' . http_build_query ( $query_parameters ) : '' );
$path_no_sharer = $path . '?' . http_build_query ( array_merge ( $query_parameters , [ 'no_sharer' => true ]));
DI :: page ()[ 'aside' ] .= Renderer :: replaceMacros ( Renderer :: getMarkupTemplate ( 'widget/community_sharer.tpl' ), [
'$title' => DI :: l10n () -> t ( 'Own Contacts' ),
'$path_all' => $path_all ,
'$path_no_sharer' => $path_no_sharer ,
'$no_sharer' => ! empty ( $_REQUEST [ 'no_sharer' ]),
'$all' => DI :: l10n () -> t ( 'Include' ),
'$no_sharer_label' => DI :: l10n () -> t ( 'Hide' ),
]);
}
if ( Feature :: isEnabled ( local_user (), 'trending_tags' )) {
DI :: page ()[ 'aside' ] .= TrendingTags :: getHTML ( self :: $content );
}
// We need the editor here to be able to reshare an item.
if ( Session :: isAuthenticated ()) {
2021-09-23 17:18:36 -04:00
$o .= DI :: conversation () -> statusEditor ([], 0 , true );
2020-09-27 01:46:07 -04:00
}
}
2020-02-16 04:38:56 -05:00
$items = self :: getItems ();
if ( ! DBA :: isResult ( $items )) {
2020-07-23 02:11:21 -04:00
notice ( DI :: l10n () -> t ( 'No results.' ));
2020-02-16 04:38:56 -05:00
return $o ;
}
2021-09-23 17:18:36 -04:00
$o .= DI :: conversation () -> create ( $items , 'community' , false , false , 'commented' , local_user ());
2020-02-16 04:38:56 -05:00
$pager = new BoundariesPager (
2020-02-16 11:53:52 -05:00
DI :: l10n (),
2020-02-16 04:38:56 -05:00
DI :: args () -> getQueryString (),
$items [ 0 ][ 'commented' ],
$items [ count ( $items ) - 1 ][ 'commented' ],
self :: $itemsPerPage
);
2020-09-27 01:25:32 -04:00
if ( DI :: pConfig () -> get ( local_user (), 'system' , 'infinite_scroll' )) {
$o .= HTML :: scrollLoader ();
} else {
$o .= $pager -> renderMinimal ( count ( $items ));
}
2020-02-16 04:38:56 -05:00
$t = Renderer :: getMarkupTemplate ( " community.tpl " );
return Renderer :: replaceMacros ( $t , [
'$content' => $o ,
'$header' => '' ,
'$show_global_community_hint' => ( self :: $content == 'global' ) && DI :: config () -> get ( 'system' , 'show_global_community_hint' ),
'$global_community_hint' => DI :: l10n () -> t ( " This community stream shows all public posts received by this node. They may not reflect the opinions of this node’ s users. " )
]);
}
/**
* Computes module parameters from the request and local configuration
*
* @ throws HTTPException\BadRequestException
* @ throws HTTPException\ForbiddenException
*/
2021-11-14 17:19:25 -05:00
protected function parseRequest ()
2020-02-16 04:38:56 -05:00
{
if ( DI :: config () -> get ( 'system' , 'block_public' ) && ! Session :: isAuthenticated ()) {
throw new HTTPException\ForbiddenException ( DI :: l10n () -> t ( 'Public access denied.' ));
}
self :: $page_style = DI :: config () -> get ( 'system' , 'community_page_style' );
if ( self :: $page_style == CP_NO_INTERNAL_COMMUNITY ) {
throw new HTTPException\ForbiddenException ( DI :: l10n () -> t ( 'Access denied.' ));
}
2021-11-14 17:19:25 -05:00
self :: $accountTypeString = $_GET [ 'accounttype' ] ? ? $this -> parameters [ 'accounttype' ] ? ? '' ;
2020-10-13 22:30:40 -04:00
self :: $accountType = User :: getAccountTypeByString ( self :: $accountTypeString );
2020-02-16 04:38:56 -05:00
2021-11-14 17:19:25 -05:00
self :: $content = $this -> parameters [ 'content' ] ? ? '' ;
2020-02-16 04:38:56 -05:00
if ( ! self :: $content ) {
if ( ! empty ( DI :: config () -> get ( 'system' , 'singleuser' ))) {
// On single user systems only the global page does make sense
self :: $content = 'global' ;
} else {
// When only the global community is allowed, we use this as default
self :: $content = self :: $page_style == CP_GLOBAL_COMMUNITY ? 'global' : 'local' ;
}
}
if ( ! in_array ( self :: $content , [ 'local' , 'global' ])) {
throw new HTTPException\BadRequestException ( DI :: l10n () -> t ( 'Community option not available.' ));
}
// Check if we are allowed to display the content to visitors
2020-02-16 12:01:53 -05:00
if ( ! Session :: isAuthenticated ()) {
2020-02-16 04:38:56 -05:00
$available = self :: $page_style == CP_USERS_AND_GLOBAL ;
if ( ! $available ) {
$available = ( self :: $page_style == CP_USERS_ON_SERVER ) && ( self :: $content == 'local' );
}
if ( ! $available ) {
$available = ( self :: $page_style == CP_GLOBAL_COMMUNITY ) && ( self :: $content == 'global' );
}
if ( ! $available ) {
throw new HTTPException\ForbiddenException ( DI :: l10n () -> t ( 'Not available.' ));
}
}
if ( DI :: mode () -> isMobile ()) {
2020-02-16 13:04:26 -05:00
self :: $itemsPerPage = DI :: pConfig () -> get ( local_user (), 'system' , 'itemspage_mobile_network' ,
DI :: config () -> get ( 'system' , 'itemspage_network_mobile' ));
2020-02-16 04:38:56 -05:00
} else {
2020-02-16 13:04:26 -05:00
self :: $itemsPerPage = DI :: pConfig () -> get ( local_user (), 'system' , 'itemspage_network' ,
DI :: config () -> get ( 'system' , 'itemspage_network' ));
2020-02-16 04:38:56 -05:00
}
2020-10-07 00:01:52 -04:00
if ( ! empty ( $_GET [ 'item' ])) {
2021-01-15 23:16:09 -05:00
$item = Post :: selectFirst ([ 'parent' ], [ 'id' => $_GET [ 'item' ]]);
2020-10-07 00:01:52 -04:00
self :: $item_id = $item [ 'parent' ] ? ? 0 ;
} else {
self :: $item_id = 0 ;
}
2020-10-12 23:45:02 -04:00
self :: $min_id = $_GET [ 'min_id' ] ? ? null ;
2020-02-16 04:38:56 -05:00
self :: $max_id = $_GET [ 'max_id' ] ? ? null ;
2020-09-27 01:25:32 -04:00
self :: $max_id = $_GET [ 'last_commented' ] ? ? self :: $max_id ;
2020-02-16 04:38:56 -05:00
}
/**
* Computes the displayed items .
*
* Community pages have a restriction on how many successive posts by the same author can show on any given page ,
* so we may have to retrieve more content beyond the first query
*
* @ return array
* @ throws \Exception
*/
protected static function getItems ()
{
2020-10-12 23:45:02 -04:00
$items = self :: selectItems ( self :: $min_id , self :: $max_id , self :: $item_id , self :: $itemsPerPage );
2020-02-16 04:38:56 -05:00
$maxpostperauthor = ( int ) DI :: config () -> get ( 'system' , 'max_author_posts_community_page' );
if ( $maxpostperauthor != 0 && self :: $content == 'local' ) {
$count = 1 ;
$previousauthor = '' ;
$numposts = 0 ;
$selected_items = [];
while ( count ( $selected_items ) < self :: $itemsPerPage && ++ $count < 50 && count ( $items ) > 0 ) {
foreach ( $items as $item ) {
if ( $previousauthor == $item [ " author-link " ]) {
++ $numposts ;
} else {
$numposts = 0 ;
}
$previousauthor = $item [ " author-link " ];
if (( $numposts < $maxpostperauthor ) && ( count ( $selected_items ) < self :: $itemsPerPage )) {
$selected_items [] = $item ;
}
}
// If we're looking at a "previous page", the lookup continues forward in time because the list is
// sorted in chronologically decreasing order
2020-10-12 23:45:02 -04:00
if ( isset ( self :: $min_id )) {
self :: $min_id = $items [ 0 ][ 'commented' ];
2020-02-16 04:38:56 -05:00
} else {
// In any other case, the lookup continues backwards in time
self :: $max_id = $items [ count ( $items ) - 1 ][ 'commented' ];
}
2020-10-12 23:45:02 -04:00
$items = self :: selectItems ( self :: $min_id , self :: $max_id , self :: $item_id , self :: $itemsPerPage );
2020-02-16 04:38:56 -05:00
}
} else {
$selected_items = $items ;
}
return $selected_items ;
}
/**
* Database query for the comunity page
*
2020-10-12 23:45:02 -04:00
* @ param $min_id
2020-02-16 04:38:56 -05:00
* @ param $max_id
* @ param $itemspage
* @ return array
* @ throws \Exception
2020-02-16 13:04:26 -05:00
* @ TODO Move to repository / factory
2020-02-16 04:38:56 -05:00
*/
2020-10-12 23:45:02 -04:00
private static function selectItems ( $min_id , $max_id , $item_id , $itemspage )
2020-02-16 04:38:56 -05:00
{
if ( self :: $content == 'local' ) {
2020-10-13 22:30:40 -04:00
if ( ! is_null ( self :: $accountType )) {
2021-02-20 15:07:25 -05:00
$condition = [ " `wall` AND `origin` AND `private` = ? AND `owner-contact-type` = ? " , Item :: PUBLIC , self :: $accountType ];
2020-03-02 09:59:16 -05:00
} else {
2020-09-26 14:01:10 -04:00
$condition = [ " `wall` AND `origin` AND `private` = ? " , Item :: PUBLIC ];
2020-02-16 04:38:56 -05:00
}
} elseif ( self :: $content == 'global' ) {
2020-10-13 22:30:40 -04:00
if ( ! is_null ( self :: $accountType )) {
2021-02-20 15:07:25 -05:00
$condition = [ " `uid` = ? AND `private` = ? AND `owner-contact-type` = ? " , 0 , Item :: PUBLIC , self :: $accountType ];
2020-02-16 04:38:56 -05:00
} else {
2020-03-02 02:57:23 -05:00
$condition = [ " `uid` = ? AND `private` = ? " , 0 , Item :: PUBLIC ];
2020-02-16 04:38:56 -05:00
}
2020-03-02 09:59:16 -05:00
} else {
return [];
}
2020-02-16 04:38:56 -05:00
2020-10-12 23:45:02 -04:00
$params = [ 'order' => [ 'commented' => true ], 'limit' => $itemspage ];
2020-10-07 00:01:52 -04:00
if ( ! empty ( $item_id )) {
2021-02-25 00:13:49 -05:00
$condition [ 0 ] .= " AND `id` = ? " ;
2020-10-06 01:18:54 -04:00
$condition [] = $item_id ;
} else {
if ( local_user () && ! empty ( $_REQUEST [ 'no_sharer' ])) {
2021-02-22 14:47:08 -05:00
$condition [ 0 ] .= " AND NOT EXISTS (SELECT `uri-id` FROM `post-user` WHERE `post-user`.`uri-id` = `post-thread-user-view`.`uri-id` AND `post-user`.`uid` = ?) " ;
2020-10-06 01:18:54 -04:00
$condition [] = local_user ();
}
if ( isset ( $max_id )) {
$condition [ 0 ] .= " AND `commented` < ? " ;
$condition [] = $max_id ;
}
2020-02-16 04:38:56 -05:00
2020-10-12 23:45:02 -04:00
if ( isset ( $min_id )) {
2020-10-06 01:18:54 -04:00
$condition [ 0 ] .= " AND `commented` > ? " ;
2020-10-12 23:45:02 -04:00
$condition [] = $min_id ;
// Previous page case: we want the items closest to min_id but for that we need to reverse the query order
if ( ! isset ( $max_id )) {
$params [ 'order' ][ 'commented' ] = false ;
}
2020-10-06 01:18:54 -04:00
}
2020-02-16 04:38:56 -05:00
}
2021-01-27 05:01:42 -05:00
$r = Post :: selectThreadForUser ( 0 , [ 'uri-id' , 'commented' , 'author-link' ], $condition , $params );
2020-10-12 23:45:02 -04:00
2021-01-19 02:23:01 -05:00
$items = Post :: toArray ( $r );
2021-04-10 12:19:22 -04:00
if ( empty ( $items )) {
return [];
}
2020-10-12 23:45:02 -04:00
// Previous page case: once we get the relevant items closest to min_id, we need to restore the expected display order
if ( empty ( $item_id ) && isset ( $min_id ) && ! isset ( $max_id )) {
$items = array_reverse ( $items );
}
2020-03-02 09:59:16 -05:00
2020-10-12 23:45:02 -04:00
return $items ;
2020-02-16 04:38:56 -05:00
}
}