2021-09-23 23:18:36 +02:00
< ? php
/**
2023-01-01 15:36:24 +01:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2021-09-23 23:18:36 +02: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 />.
*
*/
namespace Friendica\Content ;
use Friendica\App ;
use Friendica\App\Arguments ;
use Friendica\App\BaseURL ;
use Friendica\BaseModule ;
use Friendica\Core\ACL ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Config\Capability\IManageConfigValues ;
2021-09-23 23:18:36 +02:00
use Friendica\Core\Hook ;
use Friendica\Core\L10n ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues ;
2021-09-23 23:18:36 +02:00
use Friendica\Core\Protocol ;
use Friendica\Core\Renderer ;
2022-10-20 21:32:54 +02:00
use Friendica\Core\Session\Capability\IHandleUserSessions ;
2021-09-23 23:18:36 +02:00
use Friendica\Core\Theme ;
use Friendica\Database\DBA ;
use Friendica\Model\Contact ;
2022-09-12 23:27:54 +02:00
use Friendica\Model\Item as ItemModel ;
2021-09-23 23:18:36 +02:00
use Friendica\Model\Post ;
2023-08-28 06:05:52 +02:00
use Friendica\Model\Post\Category ;
2021-09-23 23:18:36 +02:00
use Friendica\Model\Tag ;
use Friendica\Model\User ;
use Friendica\Model\Verb ;
2023-04-01 20:49:38 +02:00
use Friendica\Network\HTTPException\InternalServerErrorException ;
2021-09-23 23:18:36 +02:00
use Friendica\Object\Post as PostObject ;
use Friendica\Object\Thread ;
use Friendica\Protocol\Activity ;
2023-08-12 10:11:10 +02:00
use Friendica\User\Settings\Entity\UserGServer ;
use Friendica\User\Settings\Repository ;
2021-09-23 23:18:36 +02:00
use Friendica\Util\Crypto ;
use Friendica\Util\DateTimeFormat ;
use Friendica\Util\Profiler ;
use Friendica\Util\Strings ;
use Friendica\Util\Temporal ;
use Psr\Log\LoggerInterface ;
class Conversation
{
2023-09-01 23:56:59 +02:00
const MODE_CHANNEL = 'channel' ;
2023-01-12 07:25:55 +01:00
const MODE_COMMUNITY = 'community' ;
const MODE_CONTACTS = 'contacts' ;
const MODE_CONTACT_POSTS = 'contact-posts' ;
const MODE_DISPLAY = 'display' ;
const MODE_FILED = 'filed' ;
const MODE_NETWORK = 'network' ;
const MODE_NOTES = 'notes' ;
const MODE_SEARCH = 'search' ;
const MODE_PROFILE = 'profile' ;
2021-09-23 23:18:36 +02:00
/** @var Activity */
private $activity ;
/** @var L10n */
private $l10n ;
/** @var Profiler */
private $profiler ;
/** @var LoggerInterface */
private $logger ;
/** @var Item */
private $item ;
/** @var App\Arguments */
private $args ;
2021-10-26 21:44:29 +02:00
/** @var IManagePersonalConfigValues */
2021-09-23 23:18:36 +02:00
private $pConfig ;
/** @var BaseURL */
private $baseURL ;
2021-10-26 21:44:29 +02:00
/** @var IManageConfigValues */
2021-09-23 23:18:36 +02:00
private $config ;
/** @var App */
private $app ;
/** @var App\Page */
private $page ;
/** @var App\Mode */
private $mode ;
2022-10-20 21:32:54 +02:00
/** @var IHandleUserSessions */
2022-10-23 20:41:17 +02:00
private $session ;
2023-08-12 10:11:10 +02:00
/** @var Repository\UserGServer */
private $userGServer ;
2021-09-23 23:18:36 +02:00
2023-08-12 10:11:10 +02:00
public function __construct ( Repository\UserGServer $userGServer , LoggerInterface $logger , Profiler $profiler , Activity $activity , L10n $l10n , Item $item , Arguments $args , BaseURL $baseURL , IManageConfigValues $config , IManagePersonalConfigValues $pConfig , App\Page $page , App\Mode $mode , App $app , IHandleUserSessions $session )
2021-09-23 23:18:36 +02:00
{
2023-08-12 10:11:10 +02:00
$this -> activity = $activity ;
$this -> item = $item ;
$this -> config = $config ;
$this -> mode = $mode ;
$this -> baseURL = $baseURL ;
$this -> profiler = $profiler ;
$this -> logger = $logger ;
$this -> l10n = $l10n ;
$this -> args = $args ;
$this -> pConfig = $pConfig ;
$this -> page = $page ;
$this -> app = $app ;
$this -> session = $session ;
$this -> userGServer = $userGServer ;
2021-09-23 23:18:36 +02:00
}
2021-09-23 23:29:36 +02:00
/**
* Checks item to see if it is one of the builtin activities ( like / dislike , event attendance , consensus items , etc . )
*
* Increments the count of each matching activity and adds a link to the author as needed .
*
* @ param array $activity
* @ param array & $conv_responses ( already created with builtin activity structure )
* @ return void
* @ throws ImagickException
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public function builtinActivityPuller ( array $activity , array & $conv_responses )
2021-09-23 23:18:36 +02:00
{
2022-04-29 07:32:12 +02:00
$thread_parent = $activity [ 'thr-parent-row' ] ? ? [];
2021-09-23 23:29:36 +02:00
foreach ( $conv_responses as $mode => $v ) {
$sparkle = '' ;
2021-09-23 23:18:36 +02:00
2021-09-23 23:29:36 +02:00
switch ( $mode ) {
case 'like' :
$verb = Activity :: LIKE ;
break ;
case 'dislike' :
$verb = Activity :: DISLIKE ;
break ;
case 'attendyes' :
$verb = Activity :: ATTEND ;
break ;
case 'attendno' :
$verb = Activity :: ATTENDNO ;
break ;
case 'attendmaybe' :
$verb = Activity :: ATTENDMAYBE ;
break ;
case 'announce' :
$verb = Activity :: ANNOUNCE ;
break ;
default :
return ;
}
2022-09-12 23:27:54 +02:00
if ( ! empty ( $activity [ 'verb' ]) && $this -> activity -> match ( $activity [ 'verb' ], $verb ) && ( $activity [ 'gravity' ] != ItemModel :: GRAVITY_PARENT )) {
2021-09-23 23:29:36 +02:00
$author = [
2021-09-23 23:53:52 +02:00
'uid' => 0 ,
'id' => $activity [ 'author-id' ],
2021-09-23 23:29:36 +02:00
'network' => $activity [ 'author-network' ],
2023-06-18 18:49:38 +02:00
'url' => $activity [ 'author-link' ],
'alias' => $activity [ 'author-alias' ],
2021-09-23 23:29:36 +02:00
];
$url = Contact :: magicLinkByContact ( $author );
2022-11-09 01:33:08 +01:00
if ( strpos ( $url , 'contact/redir/' ) === 0 ) {
2021-09-23 23:29:36 +02:00
$sparkle = ' class="sparkle" ' ;
}
$link = '<a href="' . $url . '"' . $sparkle . '>' . htmlentities ( $activity [ 'author-name' ]) . '</a>' ;
if ( empty ( $activity [ 'thr-parent-id' ])) {
$activity [ 'thr-parent-id' ] = $activity [ 'parent-uri-id' ];
}
2022-04-29 07:32:12 +02:00
// Skip when the causer of the parent is the same as the author of the announce
2022-06-17 09:40:29 +02:00
if (( $verb == Activity :: ANNOUNCE ) && ! empty ( $thread_parent [ 'causer-id' ]) && ( $thread_parent [ 'causer-id' ] == $activity [ 'author-id' ])) {
2021-09-23 23:29:36 +02:00
continue ;
}
if ( ! isset ( $conv_responses [ $mode ][ $activity [ 'thr-parent-id' ]])) {
$conv_responses [ $mode ][ $activity [ 'thr-parent-id' ]] = [
'links' => [],
2021-09-23 23:53:52 +02:00
'self' => 0 ,
2021-09-23 23:29:36 +02:00
];
} elseif ( in_array ( $link , $conv_responses [ $mode ][ $activity [ 'thr-parent-id' ]][ 'links' ])) {
// only list each unique author once
continue ;
}
2022-10-23 20:41:17 +02:00
if ( $this -> session -> getPublicContactId () == $activity [ 'author-id' ]) {
2021-09-23 23:29:36 +02:00
$conv_responses [ $mode ][ $activity [ 'thr-parent-id' ]][ 'self' ] = 1 ;
}
$conv_responses [ $mode ][ $activity [ 'thr-parent-id' ]][ 'links' ][] = $link ;
// there can only be one activity verb per item so if we found anything, we can stop looking
return ;
}
2021-09-23 23:18:36 +02:00
}
2021-09-23 23:29:36 +02:00
}
2021-09-23 23:18:36 +02:00
2023-04-01 20:49:38 +02:00
/**
* Returns the liker phrase based on a list of likers
*
* @ param string $verb the activity verb
* @ param array $likers a list of likers
*
* @ return string the liker phrase
*
* @ throws InternalServerErrorException in case either the verb is invalid or the list of likers is empty
*/
private function getLikerPhrase ( string $verb , array $likers ) : string
2023-04-01 20:40:54 +02:00
{
$total = count ( $likers );
2023-04-01 20:49:38 +02:00
if ( $total === 0 ) {
throw new InternalServerErrorException ( sprintf ( 'There has to be at least one Liker for verb "%s"' , $verb ));
} else if ( $total === 1 ) {
$likerString = $likers [ 0 ];
2023-04-01 20:40:54 +02:00
} else {
if ( $total < $this -> config -> get ( 'system' , 'max_likers' )) {
$likerString = implode ( ', ' , array_slice ( $likers , 0 , - 1 ));
$likerString .= ' ' . $this -> l10n -> t ( 'and' ) . ' ' . $likers [ count ( $likers ) - 1 ];
} else {
$likerString = implode ( ', ' , array_slice ( $likers , 0 , $this -> config -> get ( 'system' , 'max_likers' ) - 1 ));
$likerString .= ' ' . $this -> l10n -> t ( 'and %d other people' , $total - $this -> config -> get ( 'system' , 'max_likers' ));
}
2023-04-01 20:49:38 +02:00
}
2023-04-01 20:40:54 +02:00
2023-04-01 20:49:38 +02:00
switch ( $verb ) {
case 'like' :
return $this -> l10n -> tt ( '%2$s likes this.' , '%2$s like this.' , $total , $likerString );
case 'dislike' :
return $this -> l10n -> tt ( '%2$s doesn\'t like this.' , '%2$s don\'t like this.' , $total , $likerString );
case 'attendyes' :
return $this -> l10n -> tt ( '%2$s attends.' , '%2$s attend.' , $total , $likerString );
case 'attendno' :
return $this -> l10n -> tt ( '%2$s doesn\'t attend.' , '%2$s don\'t attend.' , $total , $likerString );
case 'attendmaybe' :
return $this -> l10n -> tt ( '%2$s attends maybe.' , '%2$s attend maybe.' , $total , $likerString );
case 'announce' :
return $this -> l10n -> tt ( '%2$s reshared this.' , '%2$s reshared this.' , $total , $likerString );
default :
throw new InternalServerErrorException ( sprintf ( 'Unknown verb "%s"' , $verb ));
2023-04-01 20:40:54 +02:00
}
}
2021-09-23 23:29:36 +02:00
/**
* Format the activity text for an item / photo / video
*
2023-11-20 22:45:02 +01:00
* @ param array $links array of pre - linked names of actors
* @ param string $verb one of 'like, ' dislike ', ' attendyes ', ' attendno ', ' attendmaybe '
* @ param int $id item id
* @ param string $activity Activity URI
* @ param array $emojis Array with emoji reactions
2021-09-23 23:29:36 +02:00
* @ return string formatted text
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
2023-11-20 22:45:02 +01:00
public function formatActivity ( array $links , string $verb , int $id , string $activity , array $emojis ) : string
2021-09-24 06:46:30 +02:00
{
2021-09-23 23:29:36 +02:00
$this -> profiler -> startRecording ( 'rendering' );
$expanded = '' ;
2021-09-23 23:18:36 +02:00
2023-04-01 20:49:38 +02:00
$phrase = $this -> getLikerPhrase ( $verb , $links );
2023-11-20 22:45:02 +01:00
$total = max ( count ( $links ), $emojis [ $activity ][ 'total' ] ? ? 0 );
2021-09-23 23:29:36 +02:00
2023-04-01 20:40:54 +02:00
if ( $total > 1 ) {
$spanatts = " class= \" btn btn-link fakelink \" onclick= \" openClose(' { $verb } list- $id '); \" " ;
$explikers = $phrase ;
2021-09-23 23:29:36 +02:00
switch ( $verb ) {
case 'like' :
2023-04-02 11:34:08 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> likes this' , '<button type="button" %2$s>%1$d people</button> like this' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
case 'dislike' :
2023-04-02 11:34:08 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> doesn\'t like this' , '<button type="button" %2$s>%1$d peiple</button> don\'t like this' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
case 'attendyes' :
2023-04-02 11:34:08 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> attends' , '<button type="button" %2$s>%1$d people</button> attend' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
case 'attendno' :
2023-06-18 18:49:38 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> doesn\'t attend' , '<button type="button" %2$s>%1$d people</button> don\'t attend' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
case 'attendmaybe' :
2023-04-02 11:34:08 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> attends maybe' , '<button type="button" %2$s>%1$d people</button> attend maybe' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
case 'announce' :
2023-04-02 11:34:08 +02:00
$phrase = $this -> l10n -> tt ( '<button type="button" %2$s>%1$d person</button> reshared this' , '<button type="button" %2$s>%1$d people</button> reshared this' , $total , $spanatts );
2021-09-23 23:29:36 +02:00
break ;
}
2022-10-18 14:29:50 +02:00
$expanded .= " \t " . '<p class="wall-item-' . $verb . '-expanded" id="' . $verb . 'list-' . $id . '" style="display: none;" >' . $explikers . '</p>' ;
2021-09-23 23:18:36 +02:00
}
2023-04-01 20:40:54 +02:00
$output = Renderer :: replaceMacros ( Renderer :: getMarkupTemplate ( 'voting_fakelink.tpl' ), [
2021-09-23 23:29:36 +02:00
'$phrase' => $phrase ,
2021-09-24 06:46:30 +02:00
'$type' => $verb ,
'$id' => $id
2021-09-23 23:29:36 +02:00
]);
2023-04-01 20:40:54 +02:00
$output .= $expanded ;
2021-09-23 23:29:36 +02:00
$this -> profiler -> stopRecording ();
2023-04-01 20:40:54 +02:00
return $output ;
2021-09-23 23:29:36 +02:00
}
2022-06-16 18:28:38 +02:00
public function statusEditor ( array $x = [], int $notes_cid = 0 , bool $popup = false ) : string
2021-09-23 23:29:36 +02:00
{
$user = User :: getById ( $this -> app -> getLoggedInUserId (), [ 'uid' , 'nickname' , 'allow_location' , 'default-location' ]);
if ( empty ( $user [ 'uid' ])) {
return '' ;
}
$this -> profiler -> startRecording ( 'rendering' );
$o = '' ;
2021-09-26 06:16:21 +02:00
$x [ 'allow_location' ] = $x [ 'allow_location' ] ? ? $user [ 'allow_location' ];
2021-09-23 23:29:36 +02:00
$x [ 'default_location' ] = $x [ 'default_location' ] ? ? $user [ 'default-location' ];
2021-09-26 06:16:21 +02:00
$x [ 'nickname' ] = $x [ 'nickname' ] ? ? $user [ 'nickname' ];
$x [ 'lockstate' ] = $x [ 'lockstate' ] ? ? ACL :: getLockstateForUserId ( $user [ 'uid' ]) ? 'lock' : 'unlock' ;
$x [ 'acl' ] = $x [ 'acl' ] ? ? ACL :: getFullSelectorHTML ( $this -> page , $user [ 'uid' ], true );
$x [ 'bang' ] = $x [ 'bang' ] ? ? '' ;
$x [ 'visitor' ] = $x [ 'visitor' ] ? ? 'block' ;
$x [ 'is_owner' ] = $x [ 'is_owner' ] ? ? true ;
2022-10-23 20:41:17 +02:00
$x [ 'profile_uid' ] = $x [ 'profile_uid' ] ? ? $this -> session -> getLocalUserId ();
2021-09-23 23:29:36 +02:00
$geotag = ! empty ( $x [ 'allow_location' ]) ? Renderer :: replaceMacros ( Renderer :: getMarkupTemplate ( 'jot_geotag.tpl' ), []) : '' ;
$tpl = Renderer :: getMarkupTemplate ( 'jot-header.tpl' );
$this -> page [ 'htmlhead' ] .= Renderer :: replaceMacros ( $tpl , [
'$newpost' => 'true' ,
2023-02-18 20:57:30 +01:00
'$baseurl' => $this -> baseURL ,
2021-09-23 23:29:36 +02:00
'$geotag' => $geotag ,
'$nickname' => $x [ 'nickname' ],
'$ispublic' => $this -> l10n -> t ( 'Visible to <strong>everybody</strong>' ),
'$linkurl' => $this -> l10n -> t ( 'Please enter a image/video/audio/webpage URL:' ),
'$term' => $this -> l10n -> t ( 'Tag term:' ),
'$fileas' => $this -> l10n -> t ( 'Save to Folder:' ),
'$whereareu' => $this -> l10n -> t ( 'Where are you right now?' ),
'$delitems' => $this -> l10n -> t ( " Delete item \x28 s \x29 ? " ),
'$is_mobile' => $this -> mode -> isMobile (),
]);
$jotplugins = '' ;
Hook :: callAll ( 'jot_tool' , $jotplugins );
2022-04-13 07:31:32 +02:00
if ( $this -> config -> get ( 'system' , 'set_creation_date' )) {
$created_at = Temporal :: getDateTimeField (
new \DateTime ( DBA :: NULL_DATETIME ),
new \DateTime ( 'now' ),
null ,
$this -> l10n -> t ( 'Created at' ),
'created_at'
);
} else {
$created_at = '' ;
}
2022-10-16 17:24:50 +02:00
$tpl = Renderer :: getMarkupTemplate ( 'jot.tpl' );
2021-09-23 23:29:36 +02:00
$o .= Renderer :: replaceMacros ( $tpl , [
2021-09-23 23:53:52 +02:00
'$new_post' => $this -> l10n -> t ( 'New Post' ),
'$return_path' => $this -> args -> getQueryString (),
'$action' => 'item' ,
'$share' => ( $x [ 'button' ] ? ? '' ) ? : $this -> l10n -> t ( 'Share' ),
'$loading' => $this -> l10n -> t ( 'Loading...' ),
'$upload' => $this -> l10n -> t ( 'Upload photo' ),
'$shortupload' => $this -> l10n -> t ( 'upload photo' ),
'$attach' => $this -> l10n -> t ( 'Attach file' ),
'$shortattach' => $this -> l10n -> t ( 'attach file' ),
'$edbold' => $this -> l10n -> t ( 'Bold' ),
'$editalic' => $this -> l10n -> t ( 'Italic' ),
'$eduline' => $this -> l10n -> t ( 'Underline' ),
'$edquote' => $this -> l10n -> t ( 'Quote' ),
2023-05-04 12:54:29 +02:00
'$edemojis' => $this -> l10n -> t ( 'Add emojis' ),
2023-06-15 20:02:54 +02:00
'$contentwarn' => $this -> l10n -> t ( 'Content Warning' ),
2021-09-23 23:53:52 +02:00
'$edcode' => $this -> l10n -> t ( 'Code' ),
'$edimg' => $this -> l10n -> t ( 'Image' ),
'$edurl' => $this -> l10n -> t ( 'Link' ),
'$edattach' => $this -> l10n -> t ( 'Link or Media' ),
'$edvideo' => $this -> l10n -> t ( 'Video' ),
'$setloc' => $this -> l10n -> t ( 'Set your location' ),
'$shortsetloc' => $this -> l10n -> t ( 'set location' ),
'$noloc' => $this -> l10n -> t ( 'Clear browser location' ),
'$shortnoloc' => $this -> l10n -> t ( 'clear location' ),
'$title' => $x [ 'title' ] ? ? '' ,
'$placeholdertitle' => $this -> l10n -> t ( 'Set title' ),
'$category' => $x [ 'category' ] ? ? '' ,
2022-10-23 20:41:17 +02:00
'$placeholdercategory' => Feature :: isEnabled ( $this -> session -> getLocalUserId (), 'categories' ) ? $this -> l10n -> t ( " Categories \x28 comma-separated list \x29 " ) : '' ,
2021-09-23 23:53:52 +02:00
'$scheduled_at' => Temporal :: getDateTimeField (
2021-09-23 23:29:36 +02:00
new \DateTime (),
new \DateTime ( 'now + 6 months' ),
null ,
$this -> l10n -> t ( 'Scheduled at' ),
'scheduled_at'
),
2022-04-13 07:31:32 +02:00
'$created_at' => $created_at ,
2021-09-23 23:29:36 +02:00
'$wait' => $this -> l10n -> t ( 'Please wait' ),
'$permset' => $this -> l10n -> t ( 'Permission settings' ),
'$shortpermset' => $this -> l10n -> t ( 'Permissions' ),
'$wall' => $notes_cid ? 0 : 1 ,
2022-09-12 23:31:36 +02:00
'$posttype' => $notes_cid ? ItemModel :: PT_PERSONAL_NOTE : ItemModel :: PT_ARTICLE ,
2021-09-23 23:29:36 +02:00
'$content' => $x [ 'content' ] ? ? '' ,
'$post_id' => $x [ 'post_id' ] ? ? '' ,
2023-02-18 20:57:30 +01:00
'$baseurl' => $this -> baseURL ,
2021-09-23 23:29:36 +02:00
'$defloc' => $x [ 'default_location' ],
'$visitor' => $x [ 'visitor' ],
'$pvisit' => $notes_cid ? 'none' : $x [ 'visitor' ],
'$public' => $this -> l10n -> t ( 'Public post' ),
'$lockstate' => $x [ 'lockstate' ],
'$bang' => $x [ 'bang' ],
'$profile_uid' => $x [ 'profile_uid' ],
'$preview' => $this -> l10n -> t ( 'Preview' ),
'$jotplugins' => $jotplugins ,
'$notes_cid' => $notes_cid ,
'$cancel' => $this -> l10n -> t ( 'Cancel' ),
'$rand_num' => Crypto :: randomDigits ( 12 ),
// ACL permissions box
2021-09-24 06:35:27 +02:00
'$acl' => $x [ 'acl' ],
2021-09-23 23:29:36 +02:00
//jot nav tab (used in some themes)
'$message' => $this -> l10n -> t ( 'Message' ),
'$browser' => $this -> l10n -> t ( 'Browser' ),
2022-10-16 17:49:27 +02:00
'$compose_link_title' => $this -> l10n -> t ( 'Open Compose page' ),
2022-10-23 20:41:17 +02:00
'$always_open_compose' => $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'frio' , 'always_open_compose' , false ),
2021-09-23 23:29:36 +02:00
]);
if ( $popup == true ) {
$o = '<div id="jot-popup" style="display: none;">' . $o . '</div>' ;
}
$this -> profiler -> stopRecording ();
return $o ;
2021-09-23 23:18:36 +02:00
}
/**
* " Render " a conversation or list of items for HTML display .
* There are two major forms of display :
* - Sequential or unthreaded ( " New Item View " or search results )
* - conversation view
* The $mode parameter decides between the various renderings and also
* figures out how to determine page owner and other contextual items
* that are based on unique features of the calling module .
2023-02-05 01:14:21 +01:00
* @ param array $items An array of Posts
* @ param string $mode One of self :: MODE_ *
* @ param bool $update Asynchronous update rendering
* @ param bool $preview Post preview ( no actual database record )
* @ param string $order Either " received " or " commented "
2021-09-23 23:18:36 +02:00
* @ param int $uid
* @ return string
* @ throws ImagickException
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
2023-02-05 01:14:21 +01:00
public function render ( array $items , string $mode , bool $update = false , bool $preview = false , string $order = 'commented' , int $uid = 0 ) : string
2021-09-23 23:18:36 +02:00
{
$this -> profiler -> startRecording ( 'rendering' );
$this -> page -> registerFooterScript ( Theme :: getPathForFile ( 'asset/typeahead.js/dist/typeahead.bundle.js' ));
$this -> page -> registerFooterScript ( Theme :: getPathForFile ( 'js/friendica-tagsinput/friendica-tagsinput.js' ));
$this -> page -> registerStylesheet ( Theme :: getPathForFile ( 'js/friendica-tagsinput/friendica-tagsinput.css' ));
$this -> page -> registerStylesheet ( Theme :: getPathForFile ( 'js/friendica-tagsinput/friendica-tagsinput-typeahead.css' ));
$live_update_div = '' ;
2023-08-12 10:11:10 +02:00
$userGservers = $this -> userGServer -> listIgnoredByUser ( $this -> session -> getLocalUserId ());
$ignoredGsids = array_map ( function ( UserGServer $userGServer ) {
return $userGServer -> gsid ;
}, $userGservers -> getArrayCopy ());
2023-01-12 07:25:55 +01:00
if ( $mode === self :: MODE_NETWORK ) {
2023-08-12 10:11:10 +02:00
$items = $this -> addChildren ( $items , false , $order , $uid , $mode , $ignoredGsids );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
/*
* The special div is needed for liveUpdate to kick in for this page .
* We only launch liveUpdate if you aren ' t filtering in some incompatible
* way and also you aren ' t writing a comment ( discovered in javascript ) .
*/
$live_update_div = '<div id="live-network"></div>' . " \r \n "
. " <script> var profile_uid = " . $_SESSION [ 'uid' ]
. " ; var netargs = ' " . substr ( $this -> args -> getCommand (), 8 )
. '?f='
. ( ! empty ( $_GET [ 'contactid' ]) ? '&contactid=' . rawurlencode ( $_GET [ 'contactid' ]) : '' )
2021-09-25 10:06:38 +02:00
. ( ! empty ( $_GET [ 'search' ]) ? '&search=' . rawurlencode ( $_GET [ 'search' ]) : '' )
. ( ! empty ( $_GET [ 'star' ]) ? '&star=' . rawurlencode ( $_GET [ 'star' ]) : '' )
. ( ! empty ( $_GET [ 'order' ]) ? '&order=' . rawurlencode ( $_GET [ 'order' ]) : '' )
. ( ! empty ( $_GET [ 'bmark' ]) ? '&bmark=' . rawurlencode ( $_GET [ 'bmark' ]) : '' )
. ( ! empty ( $_GET [ 'liked' ]) ? '&liked=' . rawurlencode ( $_GET [ 'liked' ]) : '' )
. ( ! empty ( $_GET [ 'conv' ]) ? '&conv=' . rawurlencode ( $_GET [ 'conv' ]) : '' )
. ( ! empty ( $_GET [ 'nets' ]) ? '&nets=' . rawurlencode ( $_GET [ 'nets' ]) : '' )
. ( ! empty ( $_GET [ 'cmin' ]) ? '&cmin=' . rawurlencode ( $_GET [ 'cmin' ]) : '' )
. ( ! empty ( $_GET [ 'cmax' ]) ? '&cmax=' . rawurlencode ( $_GET [ 'cmax' ]) : '' )
. ( ! empty ( $_GET [ 'file' ]) ? '&file=' . rawurlencode ( $_GET [ 'file' ]) : '' )
2023-09-09 19:38:09 +02:00
. ( ! empty ( $_GET [ 'channel' ]) ? '&channel=' . rawurlencode ( $_GET [ 'channel' ]) : '' )
2023-09-09 11:14:36 +02:00
. ( ! empty ( $_GET [ 'no_sharer' ]) ? '&no_sharer=' . rawurlencode ( $_GET [ 'no_sharer' ]) : '' )
. ( ! empty ( $_GET [ 'accounttype' ]) ? '&accounttype=' . rawurlencode ( $_GET [ 'accounttype' ]) : '' )
2021-09-23 23:18:36 +02:00
. " '; </script> \r \n " ;
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_PROFILE ) {
2023-08-12 10:11:10 +02:00
$items = $this -> addChildren ( $items , false , $order , $uid , $mode , $ignoredGsids );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$tab = ! empty ( $_GET [ 'tab' ]) ? trim ( $_GET [ 'tab' ]) : 'posts' ;
if ( $tab === 'posts' ) {
/*
* This is ugly , but we can ' t pass the profile_uid through the session to the ajax updater ,
* because browser prefetching might change it on us . We have to deliver it with the page .
*/
$live_update_div = '<div id="live-profile"></div>' . " \r \n "
. " <script> var profile_uid = " . $uid
. " ; var netargs = '?f='; </script> \r \n " ;
}
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_NOTES ) {
2022-10-23 20:41:17 +02:00
$items = $this -> addChildren ( $items , false , $order , $this -> session -> getLocalUserId (), $mode );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$live_update_div = '<div id="live-notes"></div>' . " \r \n "
2022-10-23 20:41:17 +02:00
. " <script> var profile_uid = " . $this -> session -> getLocalUserId ()
2022-11-15 20:11:38 +01:00
. " ; var netargs = '?f='; </script> \r \n " ;
2021-09-23 23:18:36 +02:00
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_DISPLAY ) {
2023-08-12 10:11:10 +02:00
$items = $this -> addChildren ( $items , false , $order , $uid , $mode , $ignoredGsids );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$live_update_div = '<div id="live-display"></div>' . " \r \n "
2022-10-25 08:07:42 +02:00
. " <script> var profile_uid = " . ( $this -> session -> getLocalUserId () ? : 0 ) . " ; "
2021-09-23 23:18:36 +02:00
. " </script> " ;
}
2023-09-01 23:56:59 +02:00
} elseif ( $mode === self :: MODE_CHANNEL ) {
$items = $this -> addChildren ( $items , true , $order , $uid , $mode , $ignoredGsids );
if ( ! $update ) {
$live_update_div = '<div id="live-channel"></div>' . " \r \n "
2023-09-09 14:22:40 +02:00
. " <script> var profile_uid = -1; var netargs = ' " . substr ( $this -> args -> getCommand (), 8 )
2023-09-01 23:56:59 +02:00
. '?f='
. ( ! empty ( $_GET [ 'no_sharer' ]) ? '&no_sharer=' . rawurlencode ( $_GET [ 'no_sharer' ]) : '' )
. ( ! empty ( $_GET [ 'accounttype' ]) ? '&accounttype=' . rawurlencode ( $_GET [ 'accounttype' ]) : '' )
. " '; </script> \r \n " ;
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_COMMUNITY ) {
2023-08-12 10:11:10 +02:00
$items = $this -> addChildren ( $items , true , $order , $uid , $mode , $ignoredGsids );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$live_update_div = '<div id="live-community"></div>' . " \r \n "
. " <script> var profile_uid = -1; var netargs = ' " . substr ( $this -> args -> getCommand (), 10 )
. '?f='
. ( ! empty ( $_GET [ 'no_sharer' ]) ? '&no_sharer=' . rawurlencode ( $_GET [ 'no_sharer' ]) : '' )
2022-06-07 20:52:46 +02:00
. ( ! empty ( $_GET [ 'accounttype' ]) ? '&accounttype=' . rawurlencode ( $_GET [ 'accounttype' ]) : '' )
2021-09-23 23:18:36 +02:00
. " '; </script> \r \n " ;
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_CONTACTS ) {
2023-08-12 10:11:10 +02:00
$items = $this -> addChildren ( $items , false , $order , $uid , $mode , $ignoredGsids );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$live_update_div = '<div id="live-contact"></div>' . " \r \n "
. " <script> var profile_uid = -1; var netargs = ' " . substr ( $this -> args -> getCommand (), 8 )
2023-06-18 18:49:38 +02:00
. " ?f='; </script> \r \n " ;
2021-09-23 23:18:36 +02:00
}
2023-01-12 07:25:55 +01:00
} elseif ( $mode === self :: MODE_SEARCH ) {
2021-09-23 23:18:36 +02:00
$live_update_div = '<div id="live-search"></div>' . " \r \n " ;
}
2023-11-15 22:55:54 +01:00
$page_dropping = $this -> session -> getLocalUserId () && $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'show_page_drop' , true ) && ( $this -> session -> getLocalUserId () == $uid && $mode != self :: MODE_SEARCH );
2021-09-23 23:18:36 +02:00
if ( ! $update ) {
$_SESSION [ 'return_path' ] = $this -> args -> getQueryString ();
}
$cb = [ 'items' => $items , 'mode' => $mode , 'update' => $update , 'preview' => $preview ];
Hook :: callAll ( 'conversation_start' , $cb );
$items = $cb [ 'items' ];
2023-02-05 01:14:21 +01:00
$formSecurityToken = BaseModule :: getFormSecurityToken ( 'contact_action' );
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
$threads = $this -> getThreadList ( $items , $mode , $preview , $page_dropping , $formSecurityToken );
if ( in_array ( $mode , [ self :: MODE_FILED , self :: MODE_SEARCH , self :: MODE_CONTACT_POSTS ])) {
$page_template = Renderer :: getMarkupTemplate ( 'conversation.tpl' );
} else {
$page_template = Renderer :: getMarkupTemplate ( 'threaded_conversation.tpl' );
2021-09-23 23:18:36 +02:00
}
2023-02-05 01:14:21 +01:00
$o = Renderer :: replaceMacros ( $page_template , [
'$baseurl' => $this -> baseURL ,
'$return_path' => $this -> args -> getQueryString (),
'$live_update' => $live_update_div ,
'$remove' => $this -> l10n -> t ( 'remove' ),
'$mode' => $mode ,
'$update' => $update ,
'$threads' => $threads ,
'$dropping' => ( $page_dropping ? $this -> l10n -> t ( 'Delete Selected Items' ) : false ),
]);
$this -> profiler -> stopRecording ();
return $o ;
}
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
/**
* @ param array $items
* @ param string $mode One of self :: MODE_ *
* @ param bool $preview
* @ param bool $pagedrop Whether to enable the user to select the thread for deletion
* @ param string $formSecurityToken A 'contact_action' form security token
* @ return array
* @ throws InternalServerErrorException
* @ throws \ImagickException
*/
public function getThreadList ( array $items , string $mode , bool $preview , bool $pagedrop , string $formSecurityToken ) : array
{
2023-07-11 15:02:53 +02:00
if ( ! $items ) {
return [];
}
2023-02-05 01:14:21 +01:00
if ( in_array ( $mode , [ self :: MODE_FILED , self :: MODE_SEARCH , self :: MODE_CONTACT_POSTS ])) {
$threads = $this -> getContextLessThreadList ( $items , $mode , $preview , $pagedrop , $formSecurityToken );
} else {
$conv_responses = [
'like' => [],
'dislike' => [],
'attendyes' => [],
'attendno' => [],
'attendmaybe' => [],
'announce' => [],
];
if ( $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'hide_dislike' )) {
unset ( $conv_responses [ 'dislike' ]);
}
2021-09-23 23:18:36 +02:00
2023-09-01 23:56:59 +02:00
if ( in_array ( $mode , [ self :: MODE_CHANNEL , self :: MODE_COMMUNITY , self :: MODE_CONTACTS , self :: MODE_PROFILE ])) {
2021-09-23 23:18:36 +02:00
$writable = true ;
} else {
2022-06-05 22:38:05 +02:00
$writable = $items [ 0 ][ 'writable' ] || ( $items [ 0 ][ 'uid' ] == 0 ) && in_array ( $items [ 0 ][ 'network' ], Protocol :: FEDERATED );
2021-09-23 23:18:36 +02:00
}
2022-10-23 20:41:17 +02:00
if ( ! $this -> session -> getLocalUserId ()) {
2021-09-23 23:18:36 +02:00
$writable = false ;
}
2023-02-05 01:14:21 +01:00
// Normal View
$conv = new Thread ( $mode , $preview , $writable );
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
/*
* get all the topmost parents
* this shouldn ' t be needed , as we should have only them in our array
* But for now , this array respects the old style , just in case
*/
foreach ( $items as $item ) {
if ( in_array ( $item [ 'author-id' ], $this -> getBlocklist ())) {
continue ;
2021-09-23 23:18:36 +02:00
}
2023-02-05 01:14:21 +01:00
// Can we put this after the visibility check?
$this -> builtinActivityPuller ( $item , $conv_responses );
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
// Only add what is visible
if ( $item [ 'network' ] === Protocol :: MAIL && $this -> session -> getLocalUserId () != $item [ 'uid' ]) {
continue ;
}
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
if ( ! $this -> item -> isVisibleActivity ( $item )) {
continue ;
}
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
/// @todo Check if this call is needed or not
$arr = [ 'item' => $item ];
Hook :: callAll ( 'display_item' , $arr );
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
$item [ 'pagedrop' ] = $pagedrop ;
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
if ( $item [ 'gravity' ] == ItemModel :: GRAVITY_PARENT ) {
$item_object = new PostObject ( $item );
$conv -> addParent ( $item_object );
2021-09-23 23:18:36 +02:00
}
2023-02-05 01:14:21 +01:00
}
2021-09-23 23:18:36 +02:00
2023-02-05 01:14:21 +01:00
$threads = $conv -> getTemplateData ( $conv_responses , $formSecurityToken );
if ( ! $threads ) {
$this -> logger -> info ( '[ERROR] conversation : Failed to get template data.' );
$threads = [];
2021-09-23 23:18:36 +02:00
}
}
2023-02-05 01:14:21 +01:00
return $threads ;
2021-09-23 23:18:36 +02:00
}
2022-06-16 18:28:38 +02:00
private function getBlocklist () : array
2021-09-23 23:29:36 +02:00
{
2022-10-23 20:41:17 +02:00
if ( ! $this -> session -> getLocalUserId ()) {
2021-09-23 23:29:36 +02:00
return [];
}
2022-11-23 19:38:40 +01:00
$str_blocked = str_replace ([ " \n " , " \r " ], " , " , $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'blocked' ) ? ? '' );
2021-09-23 23:29:36 +02:00
if ( empty ( $str_blocked )) {
return [];
}
$blocklist = [];
foreach ( explode ( ',' , $str_blocked ) as $entry ) {
$cid = Contact :: getIdForURL ( trim ( $entry ), 0 , false );
if ( ! empty ( $cid )) {
$blocklist [] = $cid ;
}
}
return $blocklist ;
}
2021-09-23 23:18:36 +02:00
/**
* Adds some information ( Causer , post reason , direction ) to the fetched post row .
*
2022-04-29 07:32:12 +02:00
* @ param array $row Post row
* @ param array $activity Contact data of the resharer
* @ param array $thr_parent Thread parent row
2021-09-23 23:18:36 +02:00
*
* @ return array items with parents and comments
*/
2022-06-16 18:28:38 +02:00
private function addRowInformation ( array $row , array $activity , array $thr_parent ) : array
2021-09-24 06:46:30 +02:00
{
2021-09-23 23:18:36 +02:00
$this -> profiler -> startRecording ( 'rendering' );
2022-06-03 19:40:06 +02:00
if ( ! $row [ 'writable' ]) {
2021-09-23 23:18:36 +02:00
$row [ 'writable' ] = in_array ( $row [ 'network' ], Protocol :: FEDERATED );
}
if ( ! empty ( $activity )) {
2022-09-12 23:27:54 +02:00
if (( $row [ 'gravity' ] == ItemModel :: GRAVITY_PARENT )) {
2022-09-12 23:38:14 +02:00
$row [ 'post-reason' ] = ItemModel :: PR_ANNOUNCEMENT ;
2021-09-23 23:53:52 +02:00
$row = array_merge ( $row , $activity );
2021-09-23 23:18:36 +02:00
$contact = Contact :: getById ( $activity [ 'causer-id' ], [ 'url' , 'name' , 'thumb' ]);
2021-09-23 23:53:52 +02:00
$row [ 'causer-link' ] = $contact [ 'url' ];
2021-09-23 23:18:36 +02:00
$row [ 'causer-avatar' ] = $contact [ 'thumb' ];
2021-09-23 23:53:52 +02:00
$row [ 'causer-name' ] = $contact [ 'name' ];
2022-09-12 23:27:54 +02:00
} elseif (( $row [ 'gravity' ] == ItemModel :: GRAVITY_ACTIVITY ) && ( $row [ 'verb' ] == Activity :: ANNOUNCE ) &&
2023-06-18 18:49:38 +02:00
( $row [ 'author-id' ] == $activity [ 'causer-id' ])
) {
2021-09-23 23:18:36 +02:00
return $row ;
}
}
switch ( $row [ 'post-reason' ]) {
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_TO :
2021-09-23 23:18:36 +02:00
$row [ 'direction' ] = [ 'direction' => 7 , 'title' => $this -> l10n -> t ( 'You had been addressed (%s).' , 'to' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_CC :
2021-09-23 23:18:36 +02:00
$row [ 'direction' ] = [ 'direction' => 7 , 'title' => $this -> l10n -> t ( 'You had been addressed (%s).' , 'cc' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_BTO :
2021-09-23 23:18:36 +02:00
$row [ 'direction' ] = [ 'direction' => 7 , 'title' => $this -> l10n -> t ( 'You had been addressed (%s).' , 'bto' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_BCC :
2021-09-23 23:18:36 +02:00
$row [ 'direction' ] = [ 'direction' => 7 , 'title' => $this -> l10n -> t ( 'You had been addressed (%s).' , 'bcc' )];
break ;
2023-04-14 19:21:20 +02:00
case ItemModel :: PR_AUDIENCE :
$row [ 'direction' ] = [ 'direction' => 7 , 'title' => $this -> l10n -> t ( 'You had been addressed (%s).' , 'audience' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_FOLLOWER :
2022-08-10 11:28:18 +02:00
$row [ 'direction' ] = [ 'direction' => 6 , 'title' => $this -> l10n -> t ( 'You are following %s.' , $row [ 'causer-name' ] ? : $row [ 'author-name' ])];
2021-09-23 23:18:36 +02:00
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_TAG :
2023-08-28 06:05:52 +02:00
$tags = Category :: getArrayByURIId ( $row [ 'uri-id' ], $row [ 'uid' ], Category :: SUBCRIPTION );
if ( ! empty ( $tags )) {
$row [ 'direction' ] = [ 'direction' => 4 , 'title' => $this -> l10n -> t ( 'You subscribed to %s.' , implode ( ', ' , $tags ))];
} else {
$row [ 'direction' ] = [ 'direction' => 4 , 'title' => $this -> l10n -> t ( 'You subscribed to one or more tags in this post.' )];
}
2021-09-23 23:18:36 +02:00
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_ANNOUNCEMENT :
2022-10-23 20:41:17 +02:00
if ( ! empty ( $row [ 'causer-id' ]) && $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'display_resharer' )) {
2021-09-23 23:53:52 +02:00
$row [ 'owner-id' ] = $row [ 'causer-id' ];
$row [ 'owner-link' ] = $row [ 'causer-link' ];
2021-09-23 23:18:36 +02:00
$row [ 'owner-avatar' ] = $row [ 'causer-avatar' ];
2021-09-23 23:53:52 +02:00
$row [ 'owner-name' ] = $row [ 'causer-name' ];
2021-09-23 23:18:36 +02:00
}
2022-09-12 23:27:54 +02:00
if ( in_array ( $row [ 'gravity' ], [ ItemModel :: GRAVITY_PARENT , ItemModel :: GRAVITY_COMMENT ]) && ! empty ( $row [ 'causer-id' ])) {
2023-06-18 18:49:38 +02:00
$causer = [
'uid' => 0 ,
'id' => $row [ 'causer-id' ],
'network' => $row [ 'causer-network' ],
'url' => $row [ 'causer-link' ],
'alias' => $row [ 'causer-alias' ],
];
2021-09-24 06:50:47 +02:00
2023-06-18 18:49:38 +02:00
$row [ 'reshared' ] = $this -> l10n -> t ( '%s reshared this.' , '<a href="' . htmlentities ( Contact :: magicLinkByContact ( $causer )) . '">' . htmlentities ( $row [ 'causer-name' ]) . '</a>' );
2021-09-23 23:18:36 +02:00
}
$row [ 'direction' ] = [ 'direction' => 3 , 'title' => ( empty ( $row [ 'causer-id' ]) ? $this -> l10n -> t ( 'Reshared' ) : $this -> l10n -> t ( 'Reshared by %s <%s>' , $row [ 'causer-name' ], $row [ 'causer-link' ]))];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_COMMENT :
2021-09-23 23:18:36 +02:00
$row [ 'direction' ] = [ 'direction' => 5 , 'title' => $this -> l10n -> t ( '%s is participating in this thread.' , $row [ 'author-name' ])];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_STORED :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 8 , 'title' => $this -> l10n -> t ( 'Stored for general reasons' )];
2021-09-23 23:18:36 +02:00
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_GLOBAL :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 9 , 'title' => $this -> l10n -> t ( 'Global post' )];
2021-09-23 23:18:36 +02:00
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_RELAY :
2022-07-29 21:59:53 +02:00
$row [ 'direction' ] = [ 'direction' => 10 , 'title' => ( empty ( $row [ 'causer-id' ]) ? $this -> l10n -> t ( 'Sent via an relay server' ) : $this -> l10n -> t ( 'Sent via the relay server %s <%s>' , $row [ 'causer-name' ], $row [ 'causer-link' ]))];
2021-09-23 23:29:36 +02:00
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_FETCHED :
2021-09-23 23:29:36 +02:00
$row [ 'direction' ] = [ 'direction' => 2 , 'title' => ( empty ( $row [ 'causer-id' ]) ? $this -> l10n -> t ( 'Fetched' ) : $this -> l10n -> t ( 'Fetched because of %s <%s>' , $row [ 'causer-name' ], $row [ 'causer-link' ]))];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_COMPLETION :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 2 , 'title' => $this -> l10n -> t ( 'Stored because of a child post to complete this thread.' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_DIRECT :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 6 , 'title' => $this -> l10n -> t ( 'Local delivery' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_ACTIVITY :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 2 , 'title' => $this -> l10n -> t ( 'Stored because of your activity (like, comment, star, ...)' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_DISTRIBUTE :
2022-07-29 16:17:53 +02:00
$row [ 'direction' ] = [ 'direction' => 6 , 'title' => $this -> l10n -> t ( 'Distributed' )];
break ;
2022-09-12 23:38:14 +02:00
case ItemModel :: PR_PUSHED :
2022-07-29 23:28:22 +02:00
$row [ 'direction' ] = [ 'direction' => 1 , 'title' => $this -> l10n -> t ( 'Pushed to us' )];
break ;
2021-09-23 23:18:36 +02:00
}
2021-09-23 23:29:36 +02:00
2022-04-29 07:32:12 +02:00
$row [ 'thr-parent-row' ] = $thr_parent ;
2021-09-23 23:29:36 +02:00
$this -> profiler -> stopRecording ();
return $row ;
2021-09-23 23:18:36 +02:00
}
/**
2021-09-23 23:29:36 +02:00
* Add comments to top level entries that had been fetched before
2021-09-23 23:18:36 +02:00
*
2021-09-23 23:29:36 +02:00
* The system will fetch the comments for the local user whenever possible .
* This behaviour is currently needed to allow commenting on Friendica posts .
*
2022-05-28 17:55:31 +02:00
* @ param array $parents Parent items
* @ param bool $block_authors
2023-08-12 10:11:10 +02:00
* @ param string $order Either " received " or " commented "
2022-05-28 17:55:31 +02:00
* @ param int $uid
2023-08-12 10:11:10 +02:00
* @ param string $mode One of self :: MODE_ *
* @ param array $ignoredGsids List of ids of servers ignored by the user
2021-09-23 23:29:36 +02:00
* @ return array items with parents and comments
2023-08-12 10:11:10 +02:00
* @ throws InternalServerErrorException
2021-09-23 23:18:36 +02:00
*/
2023-08-12 10:11:10 +02:00
private function addChildren ( array $parents , bool $block_authors , string $order , int $uid , string $mode , array $ignoredGsids = []) : array
2021-09-24 06:46:30 +02:00
{
2021-09-23 23:18:36 +02:00
$this -> profiler -> startRecording ( 'rendering' );
2021-09-23 23:29:36 +02:00
if ( count ( $parents ) > 1 ) {
$max_comments = $this -> config -> get ( 'system' , 'max_comments' , 100 );
} else {
$max_comments = $this -> config -> get ( 'system' , 'max_display_comments' , 1000 );
}
2021-09-23 23:18:36 +02:00
2021-09-23 23:29:36 +02:00
$activities = [];
$uriids = [];
$commentcounter = [];
$activitycounter = [];
2021-09-23 23:18:36 +02:00
2021-09-24 06:35:27 +02:00
foreach ( $parents as $parent ) {
2022-09-12 23:27:54 +02:00
if ( ! empty ( $parent [ 'thr-parent-id' ]) && ! empty ( $parent [ 'gravity' ]) && ( $parent [ 'gravity' ] == ItemModel :: GRAVITY_ACTIVITY )) {
2021-09-23 23:29:36 +02:00
$uriid = $parent [ 'thr-parent-id' ];
if ( ! empty ( $parent [ 'author-id' ])) {
$activities [ $uriid ] = [ 'causer-id' => $parent [ 'author-id' ]];
foreach ([ 'commented' , 'received' , 'created' ] as $orderfields ) {
if ( ! empty ( $parent [ $orderfields ])) {
$activities [ $uriid ][ $orderfields ] = $parent [ $orderfields ];
}
}
}
} else {
$uriid = $parent [ 'uri-id' ];
2021-09-23 23:18:36 +02:00
}
2021-09-23 23:29:36 +02:00
$uriids [] = $uriid ;
2021-09-23 23:18:36 +02:00
2021-09-23 23:29:36 +02:00
$commentcounter [ $uriid ] = 0 ;
$activitycounter [ $uriid ] = 0 ;
2021-09-23 23:18:36 +02:00
}
2021-09-23 23:29:36 +02:00
$condition = [ 'parent-uri-id' => $uriids ];
if ( $block_authors ) {
$condition [ 'author-hidden' ] = false ;
2021-09-23 23:18:36 +02:00
}
2023-11-07 22:25:00 +01:00
$emojis = $this -> getEmojis ( $uriids );
$quoteshares = $this -> getQuoteShares ( $uriids );
if ( ! $this -> config -> get ( 'system' , 'legacy_activities' )) {
2023-02-18 07:56:03 +01:00
$condition = DBA :: mergeConditions ( $condition , [ " (`gravity` != ? OR `origin`) " , ItemModel :: GRAVITY_ACTIVITY ]);
}
2023-06-18 18:49:38 +02:00
$condition = DBA :: mergeConditions (
$condition ,
[ " `uid` IN (0, ?) AND (NOT `vid` IN (?, ?, ?) OR `vid` IS NULL) " , $uid , Verb :: getID ( Activity :: FOLLOW ), Verb :: getID ( Activity :: VIEW ), Verb :: getID ( Activity :: READ )]
);
2021-09-23 23:18:36 +02:00
2023-03-23 23:25:48 +01:00
$condition = DBA :: mergeConditions ( $condition , [ " (`uid` != ? OR `private` != ?) " , 0 , ItemModel :: PRIVATE ]);
2023-06-18 18:49:38 +02:00
$condition = DBA :: mergeConditions (
$condition ,
[
" `visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked`
2023-01-12 07:25:55 +01:00
AND (( NOT `contact-pending` AND ( `contact-rel` IN ( ? , ? ))) OR `self` OR `contact-uid` = ? ) " ,
2023-06-18 18:49:38 +02:00
Contact :: SHARING , Contact :: FRIEND , 0
]
);
2023-01-12 07:25:55 +01:00
2022-05-09 22:52:08 +02:00
$thread_parents = Post :: select ([ 'uri-id' , 'causer-id' ], $condition , [ 'order' => [ 'uri-id' => false , 'uid' ]]);
2022-04-30 08:19:18 +02:00
$thr_parent = [];
while ( $row = Post :: fetch ( $thread_parents )) {
$thr_parent [ $row [ 'uri-id' ]] = $row ;
}
DBA :: close ( $thread_parents );
$params = [ 'order' => [ 'uri-id' => true , 'uid' => true ]];
2023-01-12 07:25:55 +01:00
$thread_items = Post :: select ( array_merge ( ItemModel :: DISPLAY_FIELDLIST , [ 'featured' , 'contact-uid' , 'gravity' , 'post-type' , 'post-reason' ]), $condition , $params );
2021-09-23 23:18:36 +02:00
2022-12-20 20:25:57 +01:00
$items = [];
$quote_uri_ids = [];
2023-01-12 07:25:55 +01:00
$authors = [];
2021-09-23 23:18:36 +02:00
2021-09-23 23:29:36 +02:00
while ( $row = Post :: fetch ( $thread_items )) {
if ( ! empty ( $items [ $row [ 'uri-id' ]]) && ( $row [ 'uid' ] == 0 )) {
continue ;
}
2021-09-23 23:18:36 +02:00
2023-09-09 11:14:36 +02:00
if (
in_array ( $row [ 'author-gsid' ], $ignoredGsids )
2023-08-12 10:11:10 +02:00
|| in_array ( $row [ 'owner-gsid' ], $ignoredGsids )
|| in_array ( $row [ 'causer-gsid' ], $ignoredGsids )
) {
continue ;
}
2023-01-12 07:25:55 +01:00
if (( $mode != self :: MODE_CONTACTS ) && ! $row [ 'origin' ]) {
2022-05-28 17:55:31 +02:00
$row [ 'featured' ] = false ;
}
2021-09-23 23:29:36 +02:00
if ( $max_comments > 0 ) {
2022-09-12 23:27:54 +02:00
if (( $row [ 'gravity' ] == ItemModel :: GRAVITY_COMMENT ) && ( ++ $commentcounter [ $row [ 'parent-uri-id' ]] > $max_comments )) {
2021-09-23 23:29:36 +02:00
continue ;
}
2022-09-12 23:27:54 +02:00
if (( $row [ 'gravity' ] == ItemModel :: GRAVITY_ACTIVITY ) && ( ++ $activitycounter [ $row [ 'parent-uri-id' ]] > $max_comments )) {
2021-09-23 23:29:36 +02:00
continue ;
}
}
2022-04-29 07:32:12 +02:00
2023-01-12 07:25:55 +01:00
$authors [] = $row [ 'author-id' ];
$authors [] = $row [ 'owner-id' ];
2022-12-14 00:19:19 +01:00
if ( in_array ( $row [ 'gravity' ], [ ItemModel :: GRAVITY_PARENT , ItemModel :: GRAVITY_COMMENT ])) {
2022-12-20 20:25:57 +01:00
$quote_uri_ids [ $row [ 'uri-id' ]] = [
2022-12-14 00:19:19 +01:00
'uri-id' => $row [ 'uri-id' ],
'uri' => $row [ 'uri' ],
'parent-uri-id' => $row [ 'parent-uri-id' ],
'parent-uri' => $row [ 'parent-uri' ],
];
}
2022-04-30 08:19:18 +02:00
$items [ $row [ 'uri-id' ]] = $this -> addRowInformation ( $row , $activities [ $row [ 'uri-id' ]] ? ? [], $thr_parent [ $row [ 'thr-parent-id' ]] ? ? []);
2021-09-23 23:29:36 +02:00
}
2021-09-23 23:18:36 +02:00
2021-09-23 23:29:36 +02:00
DBA :: close ( $thread_items );
2021-09-23 23:18:36 +02:00
2022-12-20 20:25:57 +01:00
$quotes = Post :: select ( array_merge ( ItemModel :: DISPLAY_FIELDLIST , [ 'featured' , 'contact-uid' , 'gravity' , 'post-type' , 'post-reason' ]), [ 'quote-uri-id' => array_column ( $quote_uri_ids , 'uri-id' ), 'body' => '' , 'uid' => 0 ]);
2022-12-14 00:19:19 +01:00
while ( $quote = Post :: fetch ( $quotes )) {
$row = $quote ;
$row [ 'uid' ] = $uid ;
$row [ 'verb' ] = $row [ 'body' ] = $row [ 'raw-body' ] = Activity :: ANNOUNCE ;
$row [ 'gravity' ] = ItemModel :: GRAVITY_ACTIVITY ;
$row [ 'object-type' ] = Activity\ObjectType :: NOTE ;
2022-12-20 20:25:57 +01:00
$row [ 'parent-uri' ] = $quote_uri_ids [ $quote [ 'quote-uri-id' ]][ 'parent-uri' ];
$row [ 'parent-uri-id' ] = $quote_uri_ids [ $quote [ 'quote-uri-id' ]][ 'parent-uri-id' ];
$row [ 'thr-parent' ] = $quote_uri_ids [ $quote [ 'quote-uri-id' ]][ 'uri' ];
$row [ 'thr-parent-id' ] = $quote_uri_ids [ $quote [ 'quote-uri-id' ]][ 'uri-id' ];
2022-12-14 00:19:19 +01:00
2023-01-12 07:25:55 +01:00
$authors [] = $row [ 'author-id' ];
$authors [] = $row [ 'owner-id' ];
2022-12-14 00:19:19 +01:00
$items [ $row [ 'uri-id' ]] = $this -> addRowInformation ( $row , [], []);
}
DBA :: close ( $quotes );
2023-01-12 07:25:55 +01:00
$authors = array_unique ( $authors );
$blocks = [];
$ignores = [];
$collapses = [];
if ( ! empty ( $authors )) {
$usercontacts = DBA :: select ( 'user-contact' , [ 'cid' , 'blocked' , 'ignored' , 'collapsed' ], [ 'uid' => $uid , 'cid' => $authors ]);
while ( $usercontact = DBA :: fetch ( $usercontacts )) {
if ( $usercontact [ 'blocked' ]) {
$blocks [] = $usercontact [ 'cid' ];
}
if ( $usercontact [ 'ignored' ]) {
$ignores [] = $usercontact [ 'cid' ];
}
if ( $usercontact [ 'collapsed' ]) {
$collapses [] = $usercontact [ 'cid' ];
}
}
DBA :: close ( $usercontacts );
}
foreach ( $items as $key => $row ) {
2023-11-04 15:08:12 +01:00
$items [ $key ][ 'emojis' ] = $emojis [ $key ] ? ? [];
$items [ $key ][ 'quoteshares' ] = $quoteshares [ $key ] ? ? [];
2023-02-18 07:56:03 +01:00
2023-01-12 07:25:55 +01:00
$always_display = in_array ( $mode , [ self :: MODE_CONTACTS , self :: MODE_CONTACT_POSTS ]);
$items [ $key ][ 'user-blocked-author' ] = ! $always_display && in_array ( $row [ 'author-id' ], $blocks );
$items [ $key ][ 'user-ignored-author' ] = ! $always_display && in_array ( $row [ 'author-id' ], $ignores );
$items [ $key ][ 'user-blocked-owner' ] = ! $always_display && in_array ( $row [ 'owner-id' ], $blocks );
$items [ $key ][ 'user-ignored-owner' ] = ! $always_display && in_array ( $row [ 'owner-id' ], $ignores );
$items [ $key ][ 'user-collapsed-author' ] = ! $always_display && in_array ( $row [ 'author-id' ], $collapses );
$items [ $key ][ 'user-collapsed-owner' ] = ! $always_display && in_array ( $row [ 'owner-id' ], $collapses );
2023-06-18 18:49:38 +02:00
if (
2023-09-01 23:56:59 +02:00
in_array ( $mode , [ self :: MODE_CHANNEL , self :: MODE_COMMUNITY , self :: MODE_NETWORK ]) &&
2023-06-18 18:49:38 +02:00
( in_array ( $row [ 'author-id' ], $blocks ) || in_array ( $row [ 'owner-id' ], $blocks ) || in_array ( $row [ 'author-id' ], $ignores ) || in_array ( $row [ 'owner-id' ], $ignores ))
) {
2023-01-12 07:25:55 +01:00
unset ( $items [ $key ]);
}
}
2021-09-23 23:29:36 +02:00
$items = $this -> convSort ( $items , $order );
2021-09-23 23:18:36 +02:00
$this -> profiler -> stopRecording ();
2021-09-23 23:29:36 +02:00
return $items ;
2021-09-23 23:18:36 +02:00
}
2023-02-18 07:56:03 +01:00
/**
* Fetch emoji reaction from the conversation
*
* @ param array $uriids
* @ return array
*/
private function getEmojis ( array $uriids ) : array
{
$activity_emoji = [
Activity :: LIKE => '👍' ,
Activity :: DISLIKE => '👎' ,
Activity :: ATTEND => '✔️' ,
Activity :: ATTENDMAYBE => '❓' ,
Activity :: ATTENDNO => '❌' ,
Activity :: ANNOUNCE => '♻' ,
Activity :: VIEW => '📺' ,
];
$index_list = array_values ( $activity_emoji );
2023-11-04 13:57:54 +01:00
$verbs = array_merge ( array_keys ( $activity_emoji ), [ Activity :: EMOJIREACT , Activity :: POST ]);
2023-02-18 07:56:03 +01:00
2023-11-04 13:57:54 +01:00
$condition = DBA :: mergeConditions ([ 'parent-uri-id' => $uriids , 'gravity' => [ ItemModel :: GRAVITY_ACTIVITY , ItemModel :: GRAVITY_COMMENT ], 'verb' => $verbs ], [ " NOT `deleted` " ]);
2023-02-18 07:56:03 +01:00
$separator = chr ( 255 ) . chr ( 255 ) . chr ( 255 );
2023-11-04 13:57:54 +01:00
$sql = " SELECT `thr-parent-id`, `body`, `verb`, `gravity`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`author-name`, ' " . $separator . " ', ' ') SEPARATOR ' " . $separator . " ' LIMIT 50) AS `title` FROM `post-view` WHERE " . array_shift ( $condition ) . " GROUP BY `thr-parent-id`, `verb`, `body`, `gravity` " ;
2023-02-18 07:56:03 +01:00
$emojis = [];
$rows = DBA :: p ( $sql , $condition );
while ( $row = DBA :: fetch ( $rows )) {
2023-11-04 13:57:54 +01:00
if ( $row [ 'gravity' ] == ItemModel :: GRAVITY_ACTIVITY ) {
$row [ 'verb' ] = $row [ 'body' ] ? Activity :: EMOJIREACT : $row [ 'verb' ];
$emoji = $row [ 'body' ] ? : $activity_emoji [ $row [ 'verb' ]];
} else {
$emoji = '' ;
}
2023-02-18 07:56:03 +01:00
if ( ! isset ( $index_list [ $emoji ])) {
$index_list [] = $emoji ;
}
$index = array_search ( $emoji , $index_list );
$emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'emoji' ] = $emoji ;
$emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'verb' ] = $row [ 'verb' ];
2023-02-21 22:07:41 +01:00
$emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'total' ] = ( $emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'total' ] ? ? 0 ) + $row [ 'total' ];
$emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'title' ] = array_unique ( array_merge ( $emojis [ $row [ 'thr-parent-id' ]][ $index ][ 'title' ] ? ? [], explode ( $separator , $row [ 'title' ])));
2023-02-18 07:56:03 +01:00
}
DBA :: close ( $rows );
return $emojis ;
}
2023-11-04 15:08:12 +01:00
/**
* Fetch quote shares from the conversation
*
* @ param array $uriids
* @ return array
*/
private function getQuoteShares ( array $uriids ) : array
{
$condition = DBA :: mergeConditions ([ 'quote-uri-id' => $uriids ], [ " NOT `quote-uri-id` IS NULL " ]);
$separator = chr ( 255 ) . chr ( 255 ) . chr ( 255 );
$sql = " SELECT `quote-uri-id`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`name`, ' " . $separator . " ', ' ') SEPARATOR ' " . $separator . " ' LIMIT 50) AS `title` FROM `post-content` INNER JOIN `post` ON `post`.`uri-id` = `post-content`.`uri-id` INNER JOIN `contact` ON `post`.`author-id` = `contact`.`id` WHERE " . array_shift ( $condition ) . " GROUP BY `quote-uri-id` " ;
$quotes = [];
$rows = DBA :: p ( $sql , $condition );
while ( $row = DBA :: fetch ( $rows )) {
$quotes [ $row [ 'quote-uri-id' ]][ 'total' ] = $row [ 'total' ];
$quotes [ $row [ 'quote-uri-id' ]][ 'title' ] = array_unique ( explode ( $separator , $row [ 'title' ]));
}
DBA :: close ( $rows );
return $quotes ;
}
2021-09-23 23:18:36 +02:00
/**
* Plucks the children of the given parent from a given item list .
*
* @ param array $item_list
* @ param array $parent
* @ param bool $recursive
* @ return array
*/
2022-06-16 18:28:38 +02:00
private function getItemChildren ( array & $item_list , array $parent , bool $recursive = true ) : array
2021-09-23 23:18:36 +02:00
{
$this -> profiler -> startRecording ( 'rendering' );
$children = [];
foreach ( $item_list as $i => $item ) {
2022-09-12 23:27:54 +02:00
if ( $item [ 'gravity' ] != ItemModel :: GRAVITY_PARENT ) {
2021-09-23 23:18:36 +02:00
if ( $recursive ) {
// Fallback to parent-uri if thr-parent is not set
$thr_parent = $item [ 'thr-parent-id' ];
if ( $thr_parent == '' ) {
$thr_parent = $item [ 'parent-uri-id' ];
}
if ( $thr_parent == $parent [ 'uri-id' ]) {
$item [ 'children' ] = $this -> getItemChildren ( $item_list , $item );
2021-09-24 06:46:30 +02:00
2021-09-23 23:18:36 +02:00
$children [] = $item ;
unset ( $item_list [ $i ]);
}
} elseif ( $item [ 'parent-uri-id' ] == $parent [ 'uri-id' ]) {
$children [] = $item ;
unset ( $item_list [ $i ]);
}
}
}
$this -> profiler -> stopRecording ();
return $children ;
}
/**
* Recursively sorts a tree - like item array
*
* @ param array $items
* @ return array
*/
2022-06-16 18:28:38 +02:00
private function sortItemChildren ( array $items ) : array
2021-09-23 23:18:36 +02:00
{
$this -> profiler -> startRecording ( 'rendering' );
$result = $items ;
usort ( $result , [ $this , 'sortThrReceivedRev' ]);
foreach ( $result as $k => $i ) {
if ( isset ( $result [ $k ][ 'children' ])) {
$result [ $k ][ 'children' ] = $this -> sortItemChildren ( $result [ $k ][ 'children' ]);
}
}
$this -> profiler -> stopRecording ();
return $result ;
}
/**
* Recursively add all children items at the top level of a list
*
* @ param array $children List of items to append
* @ param array $item_list
*/
private function addChildrenToList ( array $children , array & $item_list )
{
foreach ( $children as $child ) {
$item_list [] = $child ;
if ( isset ( $child [ 'children' ])) {
$this -> addChildrenToList ( $child [ 'children' ], $item_list );
}
}
}
/**
* Selectively flattens a tree - like item structure to prevent threading stairs
*
* This recursive function takes the item tree structure created by conv_sort () and
* flatten the extraneous depth levels when people reply sequentially , removing the
* stairs effect in threaded conversations limiting the available content width .
*
* The basic principle is the following : if a post item has only one reply and is
* the last reply of its parent , then the reply is moved to the parent .
*
* This process is rendered somewhat more complicated because items can be either
* replies or likes , and these don ' t factor at all in the reply count / last reply .
*
* @ param array $parent A tree - like array of items
* @ return array
*/
2022-06-16 18:28:38 +02:00
private function smartFlattenConversation ( array $parent ) : array
2021-09-23 23:18:36 +02:00
{
$this -> profiler -> startRecording ( 'rendering' );
if ( ! isset ( $parent [ 'children' ]) || count ( $parent [ 'children' ]) == 0 ) {
$this -> profiler -> stopRecording ();
return $parent ;
}
// We use a for loop to ensure we process the newly-moved items
for ( $i = 0 ; $i < count ( $parent [ 'children' ]); $i ++ ) {
$child = $parent [ 'children' ][ $i ];
if ( isset ( $child [ 'children' ]) && count ( $child [ 'children' ])) {
// This helps counting only the regular posts
2021-09-24 06:46:30 +02:00
$count_post_closure = function ( $var ) {
2021-09-23 23:18:36 +02:00
$this -> profiler -> stopRecording ();
return $var [ 'verb' ] === Activity :: POST ;
};
$child_post_count = count ( array_filter ( $child [ 'children' ], $count_post_closure ));
$remaining_post_count = count ( array_filter ( array_slice ( $parent [ 'children' ], $i ), $count_post_closure ));
// If there's only one child's children post and this is the last child post
if ( $child_post_count == 1 && $remaining_post_count == 1 ) {
// Searches the post item in the children
$j = 0 ;
2021-09-24 06:46:30 +02:00
while ( $child [ 'children' ][ $j ][ 'verb' ] !== Activity :: POST && $j < count ( $child [ 'children' ])) {
2023-06-18 18:49:38 +02:00
$j ++ ;
2021-09-23 23:18:36 +02:00
}
$moved_item = $child [ 'children' ][ $j ];
unset ( $parent [ 'children' ][ $i ][ 'children' ][ $j ]);
$parent [ 'children' ][] = $moved_item ;
} else {
$parent [ 'children' ][ $i ] = $this -> smartFlattenConversation ( $child );
}
}
}
$this -> profiler -> stopRecording ();
return $parent ;
}
/**
* Expands a flat list of items into corresponding tree - like conversation structures .
*
* sort the top - level posts either on " received " or " commented " , and finally
* append all the items at the top level ( ? ? ? )
*
* @ param array $item_list A list of items belonging to one or more conversations
* @ param string $order Either on " received " or " commented "
* @ return array
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
2022-06-16 18:28:38 +02:00
private function convSort ( array $item_list , string $order ) : array
2021-09-23 23:18:36 +02:00
{
$this -> profiler -> startRecording ( 'rendering' );
$parents = [];
if ( ! ( is_array ( $item_list ) && count ( $item_list ))) {
$this -> profiler -> stopRecording ();
return $parents ;
}
$blocklist = $this -> getBlocklist ();
$item_array = [];
// Dedupes the item list on the uri to prevent infinite loops
foreach ( $item_list as $item ) {
if ( in_array ( $item [ 'author-id' ], $blocklist )) {
continue ;
}
$item_array [ $item [ 'uri-id' ]] = $item ;
}
// Extract the top level items
foreach ( $item_array as $item ) {
2022-09-12 23:27:54 +02:00
if ( $item [ 'gravity' ] == ItemModel :: GRAVITY_PARENT ) {
2021-09-23 23:18:36 +02:00
$parents [] = $item ;
}
}
if ( stristr ( $order , 'pinned_received' )) {
2022-04-07 23:52:25 +02:00
usort ( $parents , [ $this , 'sortThrFeaturedReceived' ]);
} elseif ( stristr ( $order , 'pinned_commented' )) {
usort ( $parents , [ $this , 'sortThrFeaturedCommented' ]);
2023-12-14 07:18:17 +01:00
} elseif ( stristr ( $order , 'pinned_created' )) {
usort ( $parents , [ $this , 'sortThrFeaturedCreated' ]);
2021-09-23 23:18:36 +02:00
} elseif ( stristr ( $order , 'received' )) {
usort ( $parents , [ $this , 'sortThrReceived' ]);
} elseif ( stristr ( $order , 'commented' )) {
usort ( $parents , [ $this , 'sortThrCommented' ]);
2022-04-20 23:03:33 +02:00
} elseif ( stristr ( $order , 'created' )) {
usort ( $parents , [ $this , 'sortThrCreated' ]);
2021-09-23 23:18:36 +02:00
}
/*
* Plucks children from the item_array , second pass collects eventual orphan
* items and add them as children of their top - level post .
*/
foreach ( $parents as $i => $parent ) {
2023-06-18 18:49:38 +02:00
$parents [ $i ][ 'children' ] = array_merge (
$this -> getItemChildren ( $item_array , $parent , true ),
$this -> getItemChildren ( $item_array , $parent , false )
);
2021-09-23 23:18:36 +02:00
}
foreach ( $parents as $i => $parent ) {
$parents [ $i ][ 'children' ] = $this -> sortItemChildren ( $parents [ $i ][ 'children' ]);
}
2022-10-23 20:41:17 +02:00
if ( ! $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'no_smart_threading' , 0 )) {
2021-09-23 23:18:36 +02:00
foreach ( $parents as $i => $parent ) {
$parents [ $i ] = $this -> smartFlattenConversation ( $parent );
}
}
2023-03-22 05:08:12 +01:00
/// @TODO: Stop recursively adding all children back to the top level (!!!)
2021-09-23 23:18:36 +02:00
/// However, this apparently ensures responses (likes, attendance) display (?!)
foreach ( $parents as $parent ) {
if ( count ( $parent [ 'children' ])) {
$this -> addChildrenToList ( $parent [ 'children' ], $parents );
}
}
$this -> profiler -> stopRecording ();
return $parents ;
}
/**
2022-04-07 23:52:25 +02:00
* usort () callback to sort item arrays by featured and the received key
2021-09-23 23:18:36 +02:00
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrFeaturedReceived ( array $a , array $b ) : int
2021-09-23 23:18:36 +02:00
{
2022-04-07 23:52:25 +02:00
if ( $b [ 'featured' ] && ! $a [ 'featured' ]) {
2021-09-23 23:18:36 +02:00
return 1 ;
2022-04-07 23:52:25 +02:00
} elseif ( ! $b [ 'featured' ] && $a [ 'featured' ]) {
2021-09-23 23:18:36 +02:00
return - 1 ;
}
return strcmp ( $b [ 'received' ], $a [ 'received' ]);
}
2022-04-07 23:52:25 +02:00
/**
* usort () callback to sort item arrays by featured and the received key
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrFeaturedCommented ( array $a , array $b ) : int
2022-04-07 23:52:25 +02:00
{
if ( $b [ 'featured' ] && ! $a [ 'featured' ]) {
return 1 ;
} elseif ( ! $b [ 'featured' ] && $a [ 'featured' ]) {
return - 1 ;
}
return strcmp ( $b [ 'commented' ], $a [ 'commented' ]);
}
2023-12-14 07:18:17 +01:00
/**
* usort () callback to sort item arrays by featured and the created key
*
* @ param array $a
* @ param array $b
* @ return int
*/
private function sortThrFeaturedCreated ( array $a , array $b ) : int
{
if ( $b [ 'featured' ] && ! $a [ 'featured' ]) {
return 1 ;
} elseif ( ! $b [ 'featured' ] && $a [ 'featured' ]) {
return - 1 ;
}
return strcmp ( $b [ 'created' ], $a [ 'created' ]);
}
2021-09-23 23:18:36 +02:00
/**
* usort () callback to sort item arrays by the received key
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrReceived ( array $a , array $b ) : int
2021-09-23 23:18:36 +02:00
{
return strcmp ( $b [ 'received' ], $a [ 'received' ]);
}
/**
* usort () callback to reverse sort item arrays by the received key
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrReceivedRev ( array $a , array $b ) : int
2021-09-23 23:18:36 +02:00
{
return strcmp ( $a [ 'received' ], $b [ 'received' ]);
}
/**
* usort () callback to sort item arrays by the commented key
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrCommented ( array $a , array $b ) : int
2021-09-23 23:18:36 +02:00
{
return strcmp ( $b [ 'commented' ], $a [ 'commented' ]);
}
2022-04-20 23:03:33 +02:00
/**
* usort () callback to sort item arrays by the created key
*
* @ param array $a
* @ param array $b
* @ return int
*/
2022-06-16 18:28:38 +02:00
private function sortThrCreated ( array $a , array $b ) : int
2022-04-20 23:03:33 +02:00
{
return strcmp ( $b [ 'created' ], $a [ 'created' ]);
}
2023-02-05 01:14:21 +01:00
/**
* " New Item View " on network page or search page results
* - just loop through the items and format them minimally for display
*
* @ param array $items
* @ param string $mode One of self :: MODE_ *
* @ param bool $preview Whether the display is a preview
* @ param bool $pagedrop Whether the user can select the threads for deletion
* @ param string $formSecurityToken A 'contact_action' form security token
* @ return array
* @ throws InternalServerErrorException
* @ throws \ImagickException
*/
public function getContextLessThreadList ( array $items , string $mode , bool $preview , bool $pagedrop , string $formSecurityToken ) : array
{
$threads = [];
$uriids = [];
foreach ( $items as $item ) {
if ( in_array ( $item [ 'uri-id' ], $uriids )) {
continue ;
}
$uriids [] = $item [ 'uri-id' ];
if ( ! $this -> item -> isVisibleActivity ( $item )) {
continue ;
}
if ( in_array ( $item [ 'author-id' ], $this -> getBlocklist ())) {
continue ;
}
// prevent private email from leaking.
if ( $item [ 'network' ] === Protocol :: MAIL && $this -> session -> getLocalUserId () != $item [ 'uid' ]) {
continue ;
}
$profile_name = $item [ 'author-name' ];
if ( ! empty ( $item [ 'author-link' ]) && empty ( $item [ 'author-name' ])) {
$profile_name = $item [ 'author-link' ];
}
$tags = Tag :: populateFromItem ( $item );
$author = [
'uid' => 0 ,
'id' => $item [ 'author-id' ],
'network' => $item [ 'author-network' ],
'url' => $item [ 'author-link' ],
'alias' => $item [ 'author-alias' ],
];
$profile_link = Contact :: magicLinkByContact ( $author );
$sparkle = '' ;
if ( strpos ( $profile_link , 'contact/redir/' ) === 0 ) {
$sparkle = ' sparkle' ;
}
$locate = [ 'location' => $item [ 'location' ], 'coord' => $item [ 'coord' ], 'html' => '' ];
Hook :: callAll ( 'render_location' , $locate );
$location_html = $locate [ 'html' ] ? : Strings :: escapeHtml ( $locate [ 'location' ] ? : $locate [ 'coord' ] ? : '' );
$this -> item -> localize ( $item );
if ( $mode === self :: MODE_FILED ) {
$dropping = true ;
} else {
$dropping = false ;
}
$drop = [
'dropping' => $dropping ,
'pagedrop' => $pagedrop ,
'select' => $this -> l10n -> t ( 'Select' ),
'delete' => $this -> l10n -> t ( 'Delete' ),
];
$likebuttons = [
'like' => null ,
'dislike' => null ,
'share' => null ,
'announce' => null ,
];
if ( $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'hide_dislike' )) {
unset ( $likebuttons [ 'dislike' ]);
}
$body_html = ItemModel :: prepareBody ( $item , true , $preview );
[ $categories , $folders ] = $this -> item -> determineCategoriesTerms ( $item , $this -> session -> getLocalUserId ());
if ( ! empty ( $item [ 'title' ])) {
$title = $item [ 'title' ];
} elseif ( ! empty ( $item [ 'content-warning' ]) && $this -> pConfig -> get ( $this -> session -> getLocalUserId (), 'system' , 'disable_cw' , false )) {
$title = ucfirst ( $item [ 'content-warning' ]);
} else {
$title = '' ;
}
if ( ! empty ( $item [ 'featured' ])) {
$pinned = $this -> l10n -> t ( 'Pinned item' );
} else {
$pinned = '' ;
}
$tmp_item = [
'template' => 'search_item.tpl' ,
'id' => ( $preview ? 'P0' : $item [ 'id' ]),
'guid' => ( $preview ? 'Q0' : $item [ 'guid' ]),
'commented' => $item [ 'commented' ],
'received' => $item [ 'received' ],
'created_date' => $item [ 'created' ],
'uriid' => $item [ 'uri-id' ],
2023-07-09 22:14:53 +02:00
'author_gsid' => $item [ 'author-gsid' ],
2023-02-05 01:14:21 +01:00
'network' => $item [ 'network' ],
'network_name' => ContactSelector :: networkToName ( $item [ 'author-network' ], $item [ 'author-link' ], $item [ 'network' ], $item [ 'author-gsid' ]),
'network_icon' => ContactSelector :: networkToIcon ( $item [ 'network' ], $item [ 'author-link' ], $item [ 'author-gsid' ]),
'linktitle' => $this -> l10n -> t ( 'View %s\'s profile @ %s' , $profile_name , $item [ 'author-link' ]),
'profile_url' => $profile_link ,
'item_photo_menu_html' => $this -> item -> photoMenu ( $item , $formSecurityToken ),
'name' => $profile_name ,
'sparkle' => $sparkle ,
'lock' => false ,
'thumb' => $this -> baseURL -> remove ( $this -> item -> getAuthorAvatar ( $item )),
'title' => $title ,
'body_html' => $body_html ,
'tags' => $tags [ 'tags' ],
'hashtags' => $tags [ 'hashtags' ],
'mentions' => $tags [ 'mentions' ],
'implicit_mentions' => $tags [ 'implicit_mentions' ],
'txt_cats' => $this -> l10n -> t ( 'Categories:' ),
'txt_folders' => $this -> l10n -> t ( 'Filed under:' ),
'has_cats' => (( count ( $categories )) ? 'true' : '' ),
'has_folders' => (( count ( $folders )) ? 'true' : '' ),
'categories' => $categories ,
'folders' => $folders ,
'text' => strip_tags ( $body_html ),
'localtime' => DateTimeFormat :: local ( $item [ 'created' ], 'r' ),
'utc' => DateTimeFormat :: utc ( $item [ 'created' ], 'c' ),
'ago' => (( $item [ 'app' ]) ? $this -> l10n -> t ( '%s from %s' , Temporal :: getRelativeDate ( $item [ 'created' ]), $item [ 'app' ]) : Temporal :: getRelativeDate ( $item [ 'created' ])),
'location_html' => $location_html ,
'indent' => '' ,
'owner_name' => '' ,
'owner_url' => '' ,
'owner_photo' => $this -> baseURL -> remove ( $this -> item -> getOwnerAvatar ( $item )),
'plink' => ItemModel :: getPlink ( $item ),
'edpost' => false ,
'pinned' => $pinned ,
'isstarred' => 'unstarred' ,
'star' => false ,
'drop' => $drop ,
'vote' => $likebuttons ,
'like_html' => '' ,
'dislike_html ' => '' ,
'comment_html' => '' ,
'conv' => $preview ? '' : [ 'href' => 'display/' . $item [ 'guid' ], 'title' => $this -> l10n -> t ( 'View in context' )],
'previewing' => $preview ? ' preview ' : '' ,
'wait' => $this -> l10n -> t ( 'Please wait' ),
'thread_level' => 1 ,
];
$arr = [ 'item' => $item , 'output' => $tmp_item ];
Hook :: callAll ( 'display_item' , $arr );
$threads [] = [
'id' => $item [ 'id' ],
'network' => $item [ 'network' ],
'items' => [ $arr [ 'output' ]],
];
}
return $threads ;
}
2021-09-23 23:18:36 +02:00
}